diff --git a/src/applications/almanac/query/AlmanacBindingQuery.php b/src/applications/almanac/query/AlmanacBindingQuery.php index dd4336db8c..0b6d7cda3a 100644 --- a/src/applications/almanac/query/AlmanacBindingQuery.php +++ b/src/applications/almanac/query/AlmanacBindingQuery.php @@ -1,139 +1,139 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withServicePHIDs(array $phids) { $this->servicePHIDs = $phids; return $this; } public function withDevicePHIDs(array $phids) { $this->devicePHIDs = $phids; return $this; } public function withInterfacePHIDs(array $phids) { $this->interfacePHIDs = $phids; return $this; } protected function loadPage() { $table = new AlmanacBinding(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $bindings) { $service_phids = mpull($bindings, 'getServicePHID'); $device_phids = mpull($bindings, 'getDevicePHID'); $interface_phids = mpull($bindings, 'getInterfacePHID'); $services = id(new AlmanacServiceQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($service_phids) ->execute(); $services = mpull($services, null, 'getPHID'); $devices = id(new AlmanacDeviceQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($device_phids) ->execute(); $devices = mpull($devices, null, 'getPHID'); $interfaces = id(new AlmanacInterfaceQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($interface_phids) ->execute(); $interfaces = mpull($interfaces, null, 'getPHID'); foreach ($bindings as $key => $binding) { $service = idx($services, $binding->getServicePHID()); $device = idx($devices, $binding->getDevicePHID()); $interface = idx($interfaces, $binding->getInterfacePHID()); if (!$service || !$device || !$interface) { $this->didRejectResult($binding); unset($bindings[$key]); continue; } $binding->attachService($service); $binding->attachDevice($device); $binding->attachInterface($interface); } return $bindings; } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->servicePHIDs !== null) { $where[] = qsprintf( $conn_r, 'servicePHID IN (%Ls)', $this->servicePHIDs); } if ($this->devicePHIDs !== null) { $where[] = qsprintf( $conn_r, 'devicePHID IN (%Ls)', $this->devicePHIDs); } if ($this->interfacePHIDs !== null) { $where[] = qsprintf( $conn_r, 'interfacePHID IN (%Ls)', $this->interfacePHIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } } diff --git a/src/applications/almanac/query/AlmanacDeviceQuery.php b/src/applications/almanac/query/AlmanacDeviceQuery.php index bd1797088f..287af6c647 100644 --- a/src/applications/almanac/query/AlmanacDeviceQuery.php +++ b/src/applications/almanac/query/AlmanacDeviceQuery.php @@ -1,103 +1,103 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withNames(array $names) { $this->names = $names; return $this; } public function withNamePrefix($prefix) { $this->namePrefix = $prefix; return $this; } public function withNameSuffix($suffix) { $this->nameSuffix = $suffix; return $this; } protected function loadPage() { $table = new AlmanacDevice(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->names !== null) { $hashes = array(); foreach ($this->names as $name) { $hashes[] = PhabricatorHash::digestForIndex($name); } $where[] = qsprintf( $conn_r, 'nameIndex IN (%Ls)', $hashes); } if ($this->namePrefix !== null) { $where[] = qsprintf( $conn_r, 'name LIKE %>', $this->namePrefix); } if ($this->nameSuffix !== null) { $where[] = qsprintf( $conn_r, 'name LIKE %<', $this->nameSuffix); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorAlmanacApplication'; } } diff --git a/src/applications/almanac/query/AlmanacInterfaceQuery.php b/src/applications/almanac/query/AlmanacInterfaceQuery.php index 7e2a867d29..e119203da5 100644 --- a/src/applications/almanac/query/AlmanacInterfaceQuery.php +++ b/src/applications/almanac/query/AlmanacInterfaceQuery.php @@ -1,139 +1,139 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withNetworkPHIDs(array $phids) { $this->networkPHIDs = $phids; return $this; } public function withDevicePHIDs(array $phids) { $this->devicePHIDs = $phids; return $this; } public function withAddresses(array $addresses) { $this->addresses = $addresses; return $this; } protected function loadPage() { $table = new AlmanacInterface(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $interfaces) { $network_phids = mpull($interfaces, 'getNetworkPHID'); $device_phids = mpull($interfaces, 'getDevicePHID'); $networks = id(new AlmanacNetworkQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($network_phids) ->execute(); $networks = mpull($networks, null, 'getPHID'); $devices = id(new AlmanacDeviceQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($device_phids) ->execute(); $devices = mpull($devices, null, 'getPHID'); foreach ($interfaces as $key => $interface) { $network = idx($networks, $interface->getNetworkPHID()); $device = idx($devices, $interface->getDevicePHID()); if (!$network || !$device) { $this->didRejectResult($interface); unset($interfaces[$key]); continue; } $interface->attachNetwork($network); $interface->attachDevice($device); } return $interfaces; } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->networkPHIDs !== null) { $where[] = qsprintf( $conn_r, 'networkPHID IN (%Ls)', $this->networkPHIDs); } if ($this->devicePHIDs !== null) { $where[] = qsprintf( $conn_r, 'devicePHID IN (%Ls)', $this->devicePHIDs); } if ($this->addresses !== null) { $parts = array(); foreach ($this->addresses as $address) { $parts[] = qsprintf( $conn_r, '(networkPHID = %s AND address = %s AND port = %d)', $address->getNetworkPHID(), $address->getAddress(), $address->getPort()); } $where[] = implode(' OR ', $parts); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorAlmanacApplication'; } } diff --git a/src/applications/almanac/query/AlmanacNetworkQuery.php b/src/applications/almanac/query/AlmanacNetworkQuery.php index 1eb43fa828..d33def72ea 100644 --- a/src/applications/almanac/query/AlmanacNetworkQuery.php +++ b/src/applications/almanac/query/AlmanacNetworkQuery.php @@ -1,60 +1,60 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } protected function loadPage() { $table = new AlmanacNetwork(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorAlmanacApplication'; } } diff --git a/src/applications/almanac/query/AlmanacPropertyQuery.php b/src/applications/almanac/query/AlmanacPropertyQuery.php index 470ea62618..886c26a7be 100644 --- a/src/applications/almanac/query/AlmanacPropertyQuery.php +++ b/src/applications/almanac/query/AlmanacPropertyQuery.php @@ -1,111 +1,111 @@ ids = $ids; return $this; } public function withObjectPHIDs(array $phids) { $this->objectPHIDs = $phids; return $this; } public function withNames(array $names) { $this->names = $names; return $this; } public function setDisablePolicyFilteringAndAttachment($disable) { $this->disablePolicyFilteringAndAttachment = $disable; return $this; } protected function shouldDisablePolicyFiltering() { return $this->disablePolicyFilteringAndAttachment; } protected function loadPage() { $table = new AlmanacProperty(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $properties) { if (!$this->disablePolicyFilteringAndAttachment) { $object_phids = mpull($properties, 'getObjectPHID'); $objects = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($object_phids) ->execute(); $objects = mpull($objects, null, 'getPHID'); foreach ($properties as $key => $property) { $object = idx($objects, $property->getObjectPHID()); if (!$object) { unset($properties[$key]); continue; } $property->attachObject($object); } } return $properties; } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->objectPHIDs !== null) { $where[] = qsprintf( $conn_r, 'objectPHID IN (%Ls)', $this->objectPHIDs); } if ($this->names !== null) { $hashes = array(); foreach ($this->names as $name) { $hashes[] = PhabricatorHash::digestForIndex($name); } $where[] = qsprintf( $conn_r, 'fieldIndex IN (%Ls)', $hashes); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorAlmanacApplication'; } } diff --git a/src/applications/almanac/query/AlmanacServiceQuery.php b/src/applications/almanac/query/AlmanacServiceQuery.php index d9aca2650e..d961b2e4ab 100644 --- a/src/applications/almanac/query/AlmanacServiceQuery.php +++ b/src/applications/almanac/query/AlmanacServiceQuery.php @@ -1,224 +1,224 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withNames(array $names) { $this->names = $names; return $this; } public function withServiceClasses(array $classes) { $this->serviceClasses = $classes; return $this; } public function withDevicePHIDs(array $phids) { $this->devicePHIDs = $phids; return $this; } public function withLocked($locked) { $this->locked = $locked; return $this; } public function withNamePrefix($prefix) { $this->namePrefix = $prefix; return $this; } public function withNameSuffix($suffix) { $this->nameSuffix = $suffix; return $this; } public function needBindings($need_bindings) { $this->needBindings = $need_bindings; return $this; } protected function loadPage() { $table = new AlmanacService(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT service.* FROM %T service %Q %Q %Q %Q', $table->getTableName(), $this->buildJoinClause($conn_r), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function buildJoinClause($conn_r) { $joins = array(); if ($this->devicePHIDs !== null) { $joins[] = qsprintf( $conn_r, 'JOIN %T binding ON service.phid = binding.servicePHID', id(new AlmanacBinding())->getTableName()); } return implode(' ', $joins); } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'service.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'service.phid IN (%Ls)', $this->phids); } if ($this->names !== null) { $hashes = array(); foreach ($this->names as $name) { $hashes[] = PhabricatorHash::digestForIndex($name); } $where[] = qsprintf( $conn_r, 'service.nameIndex IN (%Ls)', $hashes); } if ($this->serviceClasses !== null) { $where[] = qsprintf( $conn_r, 'service.serviceClass IN (%Ls)', $this->serviceClasses); } if ($this->devicePHIDs !== null) { $where[] = qsprintf( $conn_r, 'binding.devicePHID IN (%Ls)', $this->devicePHIDs); } if ($this->locked !== null) { $where[] = qsprintf( $conn_r, 'service.isLocked = %d', (int)$this->locked); } if ($this->namePrefix !== null) { $where[] = qsprintf( $conn_r, 'service.name LIKE %>', $this->namePrefix); } if ($this->nameSuffix !== null) { $where[] = qsprintf( $conn_r, 'service.name LIKE %<', $this->nameSuffix); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } protected function willFilterPage(array $services) { $service_types = AlmanacServiceType::getAllServiceTypes(); foreach ($services as $key => $service) { $service_class = $service->getServiceClass(); $service_type = idx($service_types, $service_class); if (!$service_type) { $this->didRejectResult($service); unset($services[$key]); continue; } $service->attachServiceType($service_type); } return $services; } protected function didFilterPage(array $services) { if ($this->needBindings) { $service_phids = mpull($services, 'getPHID'); $bindings = id(new AlmanacBindingQuery()) ->setViewer($this->getViewer()) ->withServicePHIDs($service_phids) ->execute(); $bindings = mgroup($bindings, 'getServicePHID'); foreach ($services as $service) { $service_bindings = idx($bindings, $service->getPHID(), array()); $service->attachBindings($service_bindings); } } return parent::didFilterPage($services); } public function getOrderableColumns() { return parent::getOrderableColumns() + array( 'name' => array( 'table' => 'service', 'column' => 'name', 'type' => 'string', 'unique' => true, 'reverse' => true, ), ); } protected function getValueMap($cursor, array $keys) { $service = $this->loadCursorObject($cursor); return array( 'id' => $service->getID(), 'name' => $service->getServiceName(), ); } public function getBuiltinOrders() { return array( 'name' => array( 'vector' => array('name'), 'name' => pht('Service Name'), ), ) + parent::getBuiltinOrders(); } } diff --git a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php index f6feea5041..cdfeb0ca43 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php @@ -1,111 +1,111 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withDateRange($begin, $end) { $this->rangeBegin = $begin; $this->rangeEnd = $end; return $this; } public function withInvitedPHIDs(array $phids) { $this->invitedPHIDs = $phids; return $this; } public function withCreatorPHIDs(array $phids) { $this->creatorPHIDs = $phids; return $this; } protected function loadPage() { $table = new PhabricatorCalendarEvent(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->rangeBegin) { $where[] = qsprintf( $conn_r, 'dateTo >= %d', $this->rangeBegin); } if ($this->rangeEnd) { $where[] = qsprintf( $conn_r, 'dateFrom <= %d', $this->rangeEnd); } // TODO: Currently, the creator is always the only invitee, but you can // query them separately since this won't always be true. if ($this->invitedPHIDs) { $where[] = qsprintf( $conn_r, 'userPHID IN (%Ls)', $this->invitedPHIDs); } if ($this->creatorPHIDs) { $where[] = qsprintf( $conn_r, 'userPHID IN (%Ls)', $this->creatorPHIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorCalendarApplication'; } } diff --git a/src/applications/chatlog/query/PhabricatorChatLogChannelQuery.php b/src/applications/chatlog/query/PhabricatorChatLogChannelQuery.php index 4dcc4a16a9..2aded5c11b 100644 --- a/src/applications/chatlog/query/PhabricatorChatLogChannelQuery.php +++ b/src/applications/chatlog/query/PhabricatorChatLogChannelQuery.php @@ -1,63 +1,63 @@ channels = $channels; return $this; } public function withIDs(array $channel_ids) { $this->channelIDs = $channel_ids; return $this; } protected function loadPage() { $table = new PhabricatorChatLogChannel(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T c %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $logs = $table->loadAllFromArray($data); return $logs; } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); $where[] = $this->buildPagingClause($conn_r); if ($this->channelIDs) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->channelIDs); } if ($this->channels) { $where[] = qsprintf( $conn_r, 'channelName IN (%Ls)', $this->channels); } return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorChatLogApplication'; } } diff --git a/src/applications/chatlog/query/PhabricatorChatLogQuery.php b/src/applications/chatlog/query/PhabricatorChatLogQuery.php index cb7fe53128..d174fdac90 100644 --- a/src/applications/chatlog/query/PhabricatorChatLogQuery.php +++ b/src/applications/chatlog/query/PhabricatorChatLogQuery.php @@ -1,84 +1,84 @@ channelIDs = $channel_ids; return $this; } public function withMaximumEpoch($epoch) { $this->maximumEpoch = $epoch; return $this; } protected function loadPage() { $table = new PhabricatorChatLogEvent(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T e %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $logs = $table->loadAllFromArray($data); return $logs; } protected function willFilterPage(array $events) { $channel_ids = mpull($events, 'getChannelID', 'getChannelID'); $channels = id(new PhabricatorChatLogChannelQuery()) ->setViewer($this->getViewer()) ->withIDs($channel_ids) ->execute(); $channels = mpull($channels, null, 'getID'); foreach ($events as $key => $event) { $channel = idx($channels, $event->getChannelID()); if (!$channel) { unset($events[$key]); continue; } $event->attachChannel($channel); } return $events; } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); $where[] = $this->buildPagingClause($conn_r); if ($this->maximumEpoch) { $where[] = qsprintf( $conn_r, 'epoch <= %d', $this->maximumEpoch); } if ($this->channelIDs) { $where[] = qsprintf( $conn_r, 'channelID IN (%Ld)', $this->channelIDs); } return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorChatLogApplication'; } } diff --git a/src/applications/conduit/query/PhabricatorConduitLogQuery.php b/src/applications/conduit/query/PhabricatorConduitLogQuery.php index 56934caf45..4d66cccae5 100644 --- a/src/applications/conduit/query/PhabricatorConduitLogQuery.php +++ b/src/applications/conduit/query/PhabricatorConduitLogQuery.php @@ -1,46 +1,46 @@ methods = $methods; return $this; } protected function loadPage() { $table = new PhabricatorConduitMethodCallLog(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data);; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->methods) { $where[] = qsprintf( $conn_r, 'method IN (%Ls)', $this->methods); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorConduitApplication'; } } diff --git a/src/applications/conduit/query/PhabricatorConduitTokenQuery.php b/src/applications/conduit/query/PhabricatorConduitTokenQuery.php index 563e437a4a..870043cac8 100644 --- a/src/applications/conduit/query/PhabricatorConduitTokenQuery.php +++ b/src/applications/conduit/query/PhabricatorConduitTokenQuery.php @@ -1,128 +1,128 @@ expired = $expired; return $this; } public function withIDs(array $ids) { $this->ids = $ids; return $this; } public function withObjectPHIDs(array $phids) { $this->objectPHIDs = $phids; return $this; } public function withTokens(array $tokens) { $this->tokens = $tokens; return $this; } public function withTokenTypes(array $types) { $this->tokenTypes = $types; return $this; } protected function loadPage() { $table = new PhabricatorConduitToken(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data);; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->objectPHIDs !== null) { $where[] = qsprintf( $conn_r, 'objectPHID IN (%Ls)', $this->objectPHIDs); } if ($this->tokens !== null) { $where[] = qsprintf( $conn_r, 'token IN (%Ls)', $this->tokens); } if ($this->tokenTypes !== null) { $where[] = qsprintf( $conn_r, 'tokenType IN (%Ls)', $this->tokenTypes); } if ($this->expired !== null) { if ($this->expired) { $where[] = qsprintf( $conn_r, 'expires <= %d', PhabricatorTime::getNow()); } else { $where[] = qsprintf( $conn_r, 'expires IS NULL OR expires > %d', PhabricatorTime::getNow()); } } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } protected function willFilterPage(array $tokens) { $object_phids = mpull($tokens, 'getObjectPHID'); $objects = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($object_phids) ->execute(); $objects = mpull($objects, null, 'getPHID'); foreach ($tokens as $key => $token) { $object = idx($objects, $token->getObjectPHID(), null); if (!$object) { $this->didRejectResult($token); unset($tokens[$key]); continue; } $token->attachObject($object); } return $tokens; } public function getQueryApplicationClass() { return 'PhabricatorConduitApplication'; } } diff --git a/src/applications/config/query/PhabricatorConfigEntryQuery.php b/src/applications/config/query/PhabricatorConfigEntryQuery.php index 01982ca7d0..56bf15a267 100644 --- a/src/applications/config/query/PhabricatorConfigEntryQuery.php +++ b/src/applications/config/query/PhabricatorConfigEntryQuery.php @@ -1,60 +1,60 @@ ids = $ids; return $this; } public function withPHIDs($phids) { $this->phids = $phids; return $this; } protected function loadPage() { $table = new PhabricatorConfigEntry(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorConfigApplication'; } } diff --git a/src/applications/conpherence/query/ConpherenceFulltextQuery.php b/src/applications/conpherence/query/ConpherenceFulltextQuery.php index 780c4914cf..244791076c 100644 --- a/src/applications/conpherence/query/ConpherenceFulltextQuery.php +++ b/src/applications/conpherence/query/ConpherenceFulltextQuery.php @@ -1,68 +1,68 @@ threadPHIDs = $phids; return $this; } public function withFulltext($fulltext) { $this->fulltext = $fulltext; return $this; } public function execute() { $table = new ConpherenceIndex(); $conn_r = $table->establishConnection('r'); $rows = queryfx_all( $conn_r, 'SELECT threadPHID, transactionPHID, previousTransactionPHID FROM %T i %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderByClause($conn_r), $this->buildLimitClause($conn_r)); return $rows; } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->threadPHIDs !== null) { $where[] = qsprintf( $conn_r, 'i.threadPHID IN (%Ls)', $this->threadPHIDs); } if (strlen($this->fulltext)) { $where[] = qsprintf( $conn_r, 'MATCH(i.corpus) AGAINST (%s IN BOOLEAN MODE)', $this->fulltext); } return $this->formatWhereClause($where); } private function buildOrderByClause(AphrontDatabaseConnection $conn_r) { if (strlen($this->fulltext)) { return qsprintf( $conn_r, 'ORDER BY MATCH(i.corpus) AGAINST (%s IN BOOLEAN MODE) DESC', $this->fulltext); } else { return qsprintf( $conn_r, 'ORDER BY id DESC'); } } } diff --git a/src/applications/conpherence/query/ConpherenceParticipantCountQuery.php b/src/applications/conpherence/query/ConpherenceParticipantCountQuery.php index 1d0cb81f8c..7a4f170c3a 100644 --- a/src/applications/conpherence/query/ConpherenceParticipantCountQuery.php +++ b/src/applications/conpherence/query/ConpherenceParticipantCountQuery.php @@ -1,73 +1,73 @@ withParticipantPHIDs(array($my_phid)) * ->withParticipationStatus(ConpherenceParticipationStatus::BEHIND) * ->execute(); */ final class ConpherenceParticipantCountQuery extends PhabricatorOffsetPagedQuery { private $participantPHIDs; private $participationStatus; public function withParticipantPHIDs(array $phids) { $this->participantPHIDs = $phids; return $this; } public function withParticipationStatus($participation_status) { $this->participationStatus = $participation_status; return $this; } public function execute() { $table = new ConpherenceParticipant(); $conn_r = $table->establishConnection('r'); $rows = queryfx_all( $conn_r, 'SELECT COUNT(*) as count, participantPHID '. 'FROM %T participant %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildGroupByClause($conn_r), $this->buildLimitClause($conn_r)); return ipull($rows, 'count', 'participantPHID'); } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->participantPHIDs) { $where[] = qsprintf( $conn_r, 'participantPHID IN (%Ls)', $this->participantPHIDs); } if ($this->participationStatus !== null) { $where[] = qsprintf( $conn_r, 'participationStatus = %d', $this->participationStatus); } return $this->formatWhereClause($where); } private function buildGroupByClause(AphrontDatabaseConnection $conn_r) { $group_by = qsprintf( $conn_r, 'GROUP BY participantPHID'); return $group_by; } } diff --git a/src/applications/conpherence/query/ConpherenceParticipantQuery.php b/src/applications/conpherence/query/ConpherenceParticipantQuery.php index f9c31deed6..bb879be4e4 100644 --- a/src/applications/conpherence/query/ConpherenceParticipantQuery.php +++ b/src/applications/conpherence/query/ConpherenceParticipantQuery.php @@ -1,128 +1,128 @@ withParticipantPHIDs(array($my_phid)) * ->execute(); * * - Q: What are the next set of conpherences as I scroll up (more recent) or * down (less recent) this list of conpherences? * - A: * * id(new ConpherenceParticipantQuery()) * ->withParticipantPHIDs(array($my_phid)) * ->withParticipantCursor($top_participant) * ->setOrder(ConpherenceParticipantQuery::ORDER_NEWER) * ->execute(); * * -or- * * id(new ConpherenceParticipantQuery()) * ->withParticipantPHIDs(array($my_phid)) * ->withParticipantCursor($bottom_participant) * ->setOrder(ConpherenceParticipantQuery::ORDER_OLDER) * ->execute(); * * For counts of read, un-read, or all conpherences by participant, see * @{class:ConpherenceParticipantCountQuery}. */ final class ConpherenceParticipantQuery extends PhabricatorOffsetPagedQuery { const LIMIT = 100; const ORDER_NEWER = 'newer'; const ORDER_OLDER = 'older'; private $participantPHIDs; private $participantCursor; private $order = self::ORDER_OLDER; public function withParticipantPHIDs(array $phids) { $this->participantPHIDs = $phids; return $this; } public function withParticipantCursor(ConpherenceParticipant $participant) { $this->participantCursor = $participant; return $this; } public function setOrder($order) { $this->order = $order; return $this; } public function execute() { $table = new ConpherenceParticipant(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T participant %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $participants = $table->loadAllFromArray($data); $participants = mpull($participants, null, 'getConpherencePHID'); if ($this->order == self::ORDER_NEWER) { $participants = array_reverse($participants); } return $participants; } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->participantPHIDs) { $where[] = qsprintf( $conn_r, 'participantPHID IN (%Ls)', $this->participantPHIDs); } if ($this->participantCursor) { $date_touched = $this->participantCursor->getDateTouched(); $id = $this->participantCursor->getID(); if ($this->order == self::ORDER_OLDER) { $compare_date = '<'; $compare_id = '<='; } else { $compare_date = '>'; $compare_id = '>='; } $where[] = qsprintf( $conn_r, '(dateTouched %Q %d OR (dateTouched = %d AND id %Q %d))', $compare_date, $date_touched, $date_touched, $compare_id, $id); } return $this->formatWhereClause($where); } private function buildOrderClause(AphrontDatabaseConnection $conn_r) { $order_word = ($this->order == self::ORDER_OLDER) ? 'DESC' : 'ASC'; // if these are different direction we won't get as efficient a query // see http://dev.mysql.com/doc/refman/5.5/en/order-by-optimization.html $order = qsprintf( $conn_r, 'ORDER BY dateTouched %Q, id %Q', $order_word, $order_word); return $order; } } diff --git a/src/applications/conpherence/query/ConpherenceThreadQuery.php b/src/applications/conpherence/query/ConpherenceThreadQuery.php index b5607c1061..c191a763b4 100644 --- a/src/applications/conpherence/query/ConpherenceThreadQuery.php +++ b/src/applications/conpherence/query/ConpherenceThreadQuery.php @@ -1,383 +1,383 @@ needFilePHIDs = $need_file_phids; return $this; } public function needParticipantCache($participant_cache) { $this->needParticipantCache = $participant_cache; return $this; } public function needParticipants($need) { $this->needParticipants = $need; return $this; } public function needWidgetData($need_widget_data) { $this->needWidgetData = $need_widget_data; return $this; } public function needTransactions($need_transactions) { $this->needTransactions = $need_transactions; return $this; } public function withIDs(array $ids) { $this->ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withParticipantPHIDs(array $phids) { $this->participantPHIDs = $phids; return $this; } public function withIsRoom($bool) { $this->isRoom = $bool; return $this; } public function setAfterTransactionID($id) { $this->afterTransactionID = $id; return $this; } public function setBeforeTransactionID($id) { $this->beforeTransactionID = $id; return $this; } public function setTransactionLimit($transaction_limit) { $this->transactionLimit = $transaction_limit; return $this; } public function getTransactionLimit() { return $this->transactionLimit; } protected function loadPage() { $table = new ConpherenceThread(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT conpherence_thread.* FROM %T conpherence_thread %Q %Q %Q %Q %Q', $table->getTableName(), $this->buildJoinClause($conn_r), $this->buildWhereClause($conn_r), $this->buildGroupClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $conpherences = $table->loadAllFromArray($data); if ($conpherences) { $conpherences = mpull($conpherences, null, 'getPHID'); $this->loadParticipantsAndInitHandles($conpherences); if ($this->needParticipantCache) { $this->loadCoreHandles($conpherences, 'getRecentParticipantPHIDs'); } if ($this->needWidgetData || $this->needParticipants) { $this->loadCoreHandles($conpherences, 'getParticipantPHIDs'); } if ($this->needTransactions) { $this->loadTransactionsAndHandles($conpherences); } if ($this->needFilePHIDs || $this->needWidgetData) { $this->loadFilePHIDs($conpherences); } if ($this->needWidgetData) { $this->loadWidgetData($conpherences); } } return $conpherences; } private function buildGroupClause($conn_r) { if ($this->participantPHIDs !== null) { return 'GROUP BY conpherence_thread.id'; } else { return $this->buildApplicationSearchGroupClause($conn_r); } } private function buildJoinClause($conn_r) { $joins = array(); if ($this->participantPHIDs !== null) { $joins[] = qsprintf( $conn_r, 'JOIN %T p ON p.conpherencePHID = conpherence_thread.phid', id(new ConpherenceParticipant())->getTableName()); } $viewer = $this->getViewer(); if ($this->shouldJoinForViewer($viewer)) { $joins[] = qsprintf( $conn_r, 'LEFT JOIN %T v ON v.conpherencePHID = conpherence_thread.phid '. 'AND v.participantPHID = %s', id(new ConpherenceParticipant())->getTableName(), $viewer->getPHID()); } $joins[] = $this->buildApplicationSearchJoinClause($conn_r); return implode(' ', $joins); } private function shouldJoinForViewer(PhabricatorUser $viewer) { if ($viewer->isLoggedIn() && $this->ids === null && $this->phids === null) { return true; } return false; } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); $where[] = $this->buildPagingClause($conn_r); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'conpherence_thread.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'conpherence_thread.phid IN (%Ls)', $this->phids); } if ($this->participantPHIDs !== null) { $where[] = qsprintf( $conn_r, 'p.participantPHID IN (%Ls)', $this->participantPHIDs); } if ($this->isRoom !== null) { $where[] = qsprintf( $conn_r, 'conpherence_thread.isRoom = %d', (int)$this->isRoom); } $viewer = $this->getViewer(); if ($this->shouldJoinForViewer($viewer)) { $where[] = qsprintf( $conn_r, 'conpherence_thread.isRoom = 1 OR v.participantPHID IS NOT NULL'); } else if ($this->phids === null && $this->ids === null) { $where[] = qsprintf( $conn_r, 'conpherence_thread.isRoom = 1'); } return $this->formatWhereClause($where); } private function loadParticipantsAndInitHandles(array $conpherences) { $participants = id(new ConpherenceParticipant()) ->loadAllWhere('conpherencePHID IN (%Ls)', array_keys($conpherences)); $map = mgroup($participants, 'getConpherencePHID'); foreach ($conpherences as $current_conpherence) { $conpherence_phid = $current_conpherence->getPHID(); $conpherence_participants = idx( $map, $conpherence_phid, array()); $conpherence_participants = mpull( $conpherence_participants, null, 'getParticipantPHID'); $current_conpherence->attachParticipants($conpherence_participants); $current_conpherence->attachHandles(array()); } return $this; } private function loadCoreHandles( array $conpherences, $method) { $handle_phids = array(); foreach ($conpherences as $conpherence) { $handle_phids[$conpherence->getPHID()] = $conpherence->$method(); } $flat_phids = array_mergev($handle_phids); $handles = id(new PhabricatorHandleQuery()) ->setViewer($this->getViewer()) ->withPHIDs($flat_phids) ->execute(); foreach ($handle_phids as $conpherence_phid => $phids) { $conpherence = $conpherences[$conpherence_phid]; $conpherence->attachHandles( $conpherence->getHandles() + array_select_keys($handles, $phids)); } return $this; } private function loadTransactionsAndHandles(array $conpherences) { $query = id(new ConpherenceTransactionQuery()) ->setViewer($this->getViewer()) ->withObjectPHIDs(array_keys($conpherences)) ->needHandles(true); // We have to flip these for the underyling query class. The semantics of // paging are tricky business. if ($this->afterTransactionID) { $query->setBeforeID($this->afterTransactionID); } else if ($this->beforeTransactionID) { $query->setAfterID($this->beforeTransactionID); } if ($this->getTransactionLimit()) { // fetch an extra for "show older" scenarios $query->setLimit($this->getTransactionLimit() + 1); } $transactions = $query->execute(); $transactions = mgroup($transactions, 'getObjectPHID'); foreach ($conpherences as $phid => $conpherence) { $current_transactions = idx($transactions, $phid, array()); $handles = array(); foreach ($current_transactions as $transaction) { $handles += $transaction->getHandles(); } $conpherence->attachHandles($conpherence->getHandles() + $handles); $conpherence->attachTransactions($current_transactions); } return $this; } private function loadFilePHIDs(array $conpherences) { $edge_type = PhabricatorObjectHasFileEdgeType::EDGECONST; $file_edges = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(array_keys($conpherences)) ->withEdgeTypes(array($edge_type)) ->execute(); foreach ($file_edges as $conpherence_phid => $data) { $conpherence = $conpherences[$conpherence_phid]; $conpherence->attachFilePHIDs(array_keys($data[$edge_type])); } return $this; } private function loadWidgetData(array $conpherences) { $participant_phids = array(); $file_phids = array(); foreach ($conpherences as $conpherence) { $participant_phids[] = array_keys($conpherence->getParticipants()); $file_phids[] = $conpherence->getFilePHIDs(); } $participant_phids = array_mergev($participant_phids); $file_phids = array_mergev($file_phids); $epochs = CalendarTimeUtil::getCalendarEventEpochs( $this->getViewer()); $start_epoch = $epochs['start_epoch']; $end_epoch = $epochs['end_epoch']; $statuses = id(new PhabricatorCalendarEventQuery()) ->setViewer($this->getViewer()) ->withInvitedPHIDs($participant_phids) ->withDateRange($start_epoch, $end_epoch) ->execute(); $statuses = mgroup($statuses, 'getUserPHID'); // attached files $files = array(); $file_author_phids = array(); $authors = array(); if ($file_phids) { $files = id(new PhabricatorFileQuery()) ->setViewer($this->getViewer()) ->withPHIDs($file_phids) ->execute(); $files = mpull($files, null, 'getPHID'); $file_author_phids = mpull($files, 'getAuthorPHID', 'getPHID'); $authors = id(new PhabricatorHandleQuery()) ->setViewer($this->getViewer()) ->withPHIDs($file_author_phids) ->execute(); $authors = mpull($authors, null, 'getPHID'); } foreach ($conpherences as $phid => $conpherence) { $participant_phids = array_keys($conpherence->getParticipants()); $statuses = array_select_keys($statuses, $participant_phids); $statuses = array_mergev($statuses); $statuses = msort($statuses, 'getDateFrom'); $conpherence_files = array(); $files_authors = array(); foreach ($conpherence->getFilePHIDs() as $curr_phid) { $curr_file = idx($files, $curr_phid); if (!$curr_file) { // this file was deleted or user doesn't have permission to see it // this is generally weird continue; } $conpherence_files[$curr_phid] = $curr_file; // some files don't have authors so be careful $current_author = null; $current_author_phid = idx($file_author_phids, $curr_phid); if ($current_author_phid) { $current_author = $authors[$current_author_phid]; } $files_authors[$curr_phid] = $current_author; } $widget_data = array( 'statuses' => $statuses, 'files' => $conpherence_files, 'files_authors' => $files_authors, ); $conpherence->attachWidgetData($widget_data); } return $this; } public function getQueryApplicationClass() { return 'PhabricatorConpherenceApplication'; } } diff --git a/src/applications/countdown/query/PhabricatorCountdownQuery.php b/src/applications/countdown/query/PhabricatorCountdownQuery.php index 5d04dfde6a..c5f206a4e2 100644 --- a/src/applications/countdown/query/PhabricatorCountdownQuery.php +++ b/src/applications/countdown/query/PhabricatorCountdownQuery.php @@ -1,88 +1,88 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs(array $author_phids) { $this->authorPHIDs = $author_phids; return $this; } public function withUpcoming($upcoming) { $this->upcoming = true; return $this; } protected function loadPage() { $table = new PhabricatorCountdown(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $countdowns = $table->loadAllFromArray($data); return $countdowns; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); $where[] = $this->buildPagingClause($conn_r); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs) { $where[] = qsprintf( $conn_r, 'authorPHID in (%Ls)', $this->authorPHIDs); } if ($this->upcoming) { $where[] = qsprintf( $conn_r, 'epoch >= %d', time()); } return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorCountdownApplication'; } } diff --git a/src/applications/daemon/query/PhabricatorDaemonLogQuery.php b/src/applications/daemon/query/PhabricatorDaemonLogQuery.php index 545c412f86..5822194d06 100644 --- a/src/applications/daemon/query/PhabricatorDaemonLogQuery.php +++ b/src/applications/daemon/query/PhabricatorDaemonLogQuery.php @@ -1,194 +1,194 @@ ids = $ids; return $this; } public function withoutIDs(array $ids) { $this->notIDs = $ids; return $this; } public function withStatus($status) { $this->status = $status; return $this; } public function withDaemonClasses(array $classes) { $this->daemonClasses = $classes; return $this; } public function setAllowStatusWrites($allow) { $this->allowStatusWrites = $allow; return $this; } public function withDaemonIDs(array $daemon_ids) { $this->daemonIDs = $daemon_ids; return $this; } protected function loadPage() { $table = new PhabricatorDaemonLog(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $daemons) { $unknown_delay = PhabricatorDaemonLogQuery::getTimeUntilUnknown(); $dead_delay = PhabricatorDaemonLogQuery::getTimeUntilDead(); $status_running = PhabricatorDaemonLog::STATUS_RUNNING; $status_unknown = PhabricatorDaemonLog::STATUS_UNKNOWN; $status_wait = PhabricatorDaemonLog::STATUS_WAIT; $status_exiting = PhabricatorDaemonLog::STATUS_EXITING; $status_exited = PhabricatorDaemonLog::STATUS_EXITED; $status_dead = PhabricatorDaemonLog::STATUS_DEAD; $filter = array_fuse($this->getStatusConstants()); foreach ($daemons as $key => $daemon) { $status = $daemon->getStatus(); $seen = $daemon->getDateModified(); $is_running = ($status == $status_running) || ($status == $status_wait) || ($status == $status_exiting); // If we haven't seen the daemon recently, downgrade its status to // unknown. $unknown_time = ($seen + $unknown_delay); if ($is_running && ($unknown_time < time())) { $status = $status_unknown; } // If the daemon hasn't been seen in quite a while, assume it is dead. $dead_time = ($seen + $dead_delay); if (($status == $status_unknown) && ($dead_time < time())) { $status = $status_dead; } // If we changed the daemon's status, adjust it. if ($status != $daemon->getStatus()) { $daemon->setStatus($status); // ...and write it, if we're in a context where that's reasonable. if ($this->allowStatusWrites) { $guard = AphrontWriteGuard::beginScopedUnguardedWrites(); $daemon->save(); unset($guard); } } // If the daemon no longer matches the filter, get rid of it. if ($filter) { if (empty($filter[$daemon->getStatus()])) { unset($daemons[$key]); } } } return $daemons; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->notIDs !== null) { $where[] = qsprintf( $conn_r, 'id NOT IN (%Ld)', $this->notIDs); } if ($this->getStatusConstants()) { $where[] = qsprintf( $conn_r, 'status IN (%Ls)', $this->getStatusConstants()); } if ($this->daemonClasses !== null) { $where[] = qsprintf( $conn_r, 'daemon IN (%Ls)', $this->daemonClasses); } if ($this->daemonIDs !== null) { $where[] = qsprintf( $conn_r, 'daemonID IN (%Ls)', $this->daemonIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } private function getStatusConstants() { $status = $this->status; switch ($status) { case self::STATUS_ALL: return array(); case self::STATUS_RUNNING: return array( PhabricatorDaemonLog::STATUS_RUNNING, ); case self::STATUS_ALIVE: return array( PhabricatorDaemonLog::STATUS_UNKNOWN, PhabricatorDaemonLog::STATUS_RUNNING, PhabricatorDaemonLog::STATUS_WAIT, PhabricatorDaemonLog::STATUS_EXITING, ); default: throw new Exception(pht('Unknown status "%s"!', $status)); } } public function getQueryApplicationClass() { return 'PhabricatorDaemonsApplication'; } } diff --git a/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php b/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php index 9641f33338..a9a062ffb2 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php @@ -1,86 +1,86 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withArchived($archived) { $this->archived = $archived; return $this; } public function withPanelTypes(array $types) { $this->panelTypes = $types; return $this; } protected function loadPage() { $table = new PhabricatorDashboardPanel(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->archived !== null) { $where[] = qsprintf( $conn_r, 'isArchived = %d', (int)$this->archived); } if ($this->panelTypes !== null) { $where[] = qsprintf( $conn_r, 'panelType IN (%Ls)', $this->panelTypes); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorDashboardApplication'; } } diff --git a/src/applications/dashboard/query/PhabricatorDashboardQuery.php b/src/applications/dashboard/query/PhabricatorDashboardQuery.php index d8ba184e81..324151e722 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardQuery.php @@ -1,102 +1,102 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function needPanels($need_panels) { $this->needPanels = $need_panels; return $this; } protected function loadPage() { $table = new PhabricatorDashboard(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function didFilterPage(array $dashboards) { if ($this->needPanels) { $edge_query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(mpull($dashboards, 'getPHID')) ->withEdgeTypes( array( PhabricatorDashboardDashboardHasPanelEdgeType::EDGECONST, )); $edge_query->execute(); $panel_phids = $edge_query->getDestinationPHIDs(); if ($panel_phids) { $panels = id(new PhabricatorDashboardPanelQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($panel_phids) ->execute(); $panels = mpull($panels, null, 'getPHID'); } else { $panels = array(); } foreach ($dashboards as $dashboard) { $dashboard_phids = $edge_query->getDestinationPHIDs( array($dashboard->getPHID())); $dashboard_panels = array_select_keys($panels, $dashboard_phids); $dashboard->attachPanelPHIDs($dashboard_phids); $dashboard->attachPanels($dashboard_panels); } } return $dashboards; } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorDashboardApplication'; } } diff --git a/src/applications/differential/query/DifferentialChangesetQuery.php b/src/applications/differential/query/DifferentialChangesetQuery.php index 6cb60e04e9..7b9f2e6d10 100644 --- a/src/applications/differential/query/DifferentialChangesetQuery.php +++ b/src/applications/differential/query/DifferentialChangesetQuery.php @@ -1,153 +1,153 @@ ids = $ids; return $this; } public function withDiffs(array $diffs) { assert_instances_of($diffs, 'DifferentialDiff'); $this->diffs = $diffs; return $this; } public function needAttachToDiffs($attach) { $this->needAttachToDiffs = $attach; return $this; } public function needHunks($need) { $this->needHunks = $need; return $this; } protected function willExecute() { // If we fail to load any changesets (which is possible in the case of an // empty commit) we'll never call didFilterPage(). Attach empty changeset // lists now so that we end up with the right result. if ($this->needAttachToDiffs) { foreach ($this->diffs as $diff) { $diff->attachChangesets(array()); } } } protected function loadPage() { $table = new DifferentialChangeset(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $changesets) { // First, attach all the diffs we already have. We can just do this // directly without worrying about querying for them. When we don't have // a diff, record that we need to load it. if ($this->diffs) { $have_diffs = mpull($this->diffs, null, 'getID'); } else { $have_diffs = array(); } $must_load = array(); foreach ($changesets as $key => $changeset) { $diff_id = $changeset->getDiffID(); if (isset($have_diffs[$diff_id])) { $changeset->attachDiff($have_diffs[$diff_id]); } else { $must_load[$key] = $changeset; } } // Load all the diffs we don't have. $need_diff_ids = mpull($must_load, 'getDiffID'); $more_diffs = array(); if ($need_diff_ids) { $more_diffs = id(new DifferentialDiffQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withIDs($need_diff_ids) ->execute(); $more_diffs = mpull($more_diffs, null, 'getID'); } // Attach the diffs we loaded. foreach ($must_load as $key => $changeset) { $diff_id = $changeset->getDiffID(); if (isset($more_diffs[$diff_id])) { $changeset->attachDiff($more_diffs[$diff_id]); } else { // We didn't have the diff, and could not load it (it does not exist, // or we can't see it), so filter this result out. unset($changesets[$key]); } } return $changesets; } protected function didFilterPage(array $changesets) { if ($this->needAttachToDiffs) { $changeset_groups = mgroup($changesets, 'getDiffID'); foreach ($this->diffs as $diff) { $diff_changesets = idx($changeset_groups, $diff->getID(), array()); $diff->attachChangesets($diff_changesets); } } if ($this->needHunks) { id(new DifferentialHunkQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withChangesets($changesets) ->needAttachToChangesets(true) ->execute(); } return $changesets; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->diffs !== null) { $where[] = qsprintf( $conn_r, 'diffID IN (%Ld)', mpull($this->diffs, 'getID')); } if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorDifferentialApplication'; } } diff --git a/src/applications/differential/query/DifferentialDiffQuery.php b/src/applications/differential/query/DifferentialDiffQuery.php index 3d58e12f8a..15ddc9c46b 100644 --- a/src/applications/differential/query/DifferentialDiffQuery.php +++ b/src/applications/differential/query/DifferentialDiffQuery.php @@ -1,156 +1,156 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withRevisionIDs(array $revision_ids) { $this->revisionIDs = $revision_ids; return $this; } public function needChangesets($bool) { $this->needChangesets = $bool; return $this; } public function needArcanistProjects($bool) { $this->needArcanistProjects = $bool; return $this; } protected function loadPage() { $table = new DifferentialDiff(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $diffs) { $revision_ids = array_filter(mpull($diffs, 'getRevisionID')); $revisions = array(); if ($revision_ids) { $revisions = id(new DifferentialRevisionQuery()) ->setViewer($this->getViewer()) ->withIDs($revision_ids) ->execute(); } foreach ($diffs as $key => $diff) { if (!$diff->getRevisionID()) { continue; } $revision = idx($revisions, $diff->getRevisionID()); if ($revision) { $diff->attachRevision($revision); continue; } unset($diffs[$key]); } if ($diffs && $this->needChangesets) { $diffs = $this->loadChangesets($diffs); } if ($diffs && $this->needArcanistProjects) { $diffs = $this->loadArcanistProjects($diffs); } return $diffs; } private function loadChangesets(array $diffs) { id(new DifferentialChangesetQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withDiffs($diffs) ->needAttachToDiffs(true) ->needHunks(true) ->execute(); return $diffs; } private function loadArcanistProjects(array $diffs) { $phids = array_filter(mpull($diffs, 'getArcanistProjectPHID')); $projects = array(); $project_map = array(); if ($phids) { $projects = id(new PhabricatorRepositoryArcanistProject()) ->loadAllWhere( 'phid IN (%Ls)', $phids); $project_map = mpull($projects, null, 'getPHID'); } foreach ($diffs as $diff) { $project = null; if ($diff->getArcanistProjectPHID()) { $project = idx($project_map, $diff->getArcanistProjectPHID()); } $diff->attachArcanistProject($project); } return $diffs; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->revisionIDs) { $where[] = qsprintf( $conn_r, 'revisionID IN (%Ld)', $this->revisionIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorDifferentialApplication'; } } diff --git a/src/applications/differential/query/DifferentialHunkQuery.php b/src/applications/differential/query/DifferentialHunkQuery.php index 94bc0297b3..3c9f898810 100644 --- a/src/applications/differential/query/DifferentialHunkQuery.php +++ b/src/applications/differential/query/DifferentialHunkQuery.php @@ -1,120 +1,120 @@ changesets = $changesets; return $this; } public function needAttachToChangesets($attach) { $this->shouldAttachToChangesets = $attach; return $this; } protected function willExecute() { // If we fail to load any hunks at all (for example, because all of // the requested changesets are directories or empty files and have no // hunks) we'll never call didFilterPage(), and thus never have an // opportunity to attach hunks. Attach empty hunk lists now so that we // end up with the right result. if ($this->shouldAttachToChangesets) { foreach ($this->changesets as $changeset) { $changeset->attachHunks(array()); } } } protected function loadPage() { $all_results = array(); // Load modern hunks. $table = new DifferentialModernHunk(); $conn_r = $table->establishConnection('r'); $modern_data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $modern_results = $table->loadAllFromArray($modern_data); // Now, load legacy hunks. $table = new DifferentialLegacyHunk(); $conn_r = $table->establishConnection('r'); $legacy_data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $legacy_results = $table->loadAllFromArray($legacy_data); // Strip all the IDs off since they're not unique and nothing should be // using them. return array_values(array_merge($legacy_results, $modern_results)); } protected function willFilterPage(array $hunks) { $changesets = mpull($this->changesets, null, 'getID'); foreach ($hunks as $key => $hunk) { $changeset = idx($changesets, $hunk->getChangesetID()); if (!$changeset) { unset($hunks[$key]); } $hunk->attachChangeset($changeset); } return $hunks; } protected function didFilterPage(array $hunks) { if ($this->shouldAttachToChangesets) { $hunk_groups = mgroup($hunks, 'getChangesetID'); foreach ($this->changesets as $changeset) { $hunks = idx($hunk_groups, $changeset->getID(), array()); $changeset->attachHunks($hunks); } } return $hunks; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if (!$this->changesets) { throw new Exception( pht('You must load hunks via changesets, with withChangesets()!')); } $where[] = qsprintf( $conn_r, 'changesetID IN (%Ld)', mpull($this->changesets, 'getID')); $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorDifferentialApplication'; } protected function getDefaultOrderVector() { // TODO: Do we need this? return array('-id'); } } diff --git a/src/applications/differential/query/DifferentialInlineCommentQuery.php b/src/applications/differential/query/DifferentialInlineCommentQuery.php index f605a78b02..127dcb7c7d 100644 --- a/src/applications/differential/query/DifferentialInlineCommentQuery.php +++ b/src/applications/differential/query/DifferentialInlineCommentQuery.php @@ -1,170 +1,170 @@ revisionIDs = $ids; return $this; } public function withNotDraft($not_draft) { $this->notDraft = $not_draft; return $this; } public function withIDs(array $ids) { $this->ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withViewerAndChangesetIDs($author_phid, array $ids) { $this->viewerAndChangesetIDs = array($author_phid, $ids); return $this; } public function withDraftComments($author_phid, $revision_id) { $this->draftComments = array($author_phid, $revision_id); return $this; } public function withDraftsByAuthors(array $author_phids) { $this->draftsByAuthors = $author_phids; return $this; } public function execute() { $table = new DifferentialTransactionComment(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildLimitClause($conn_r)); $comments = $table->loadAllFromArray($data); foreach ($comments as $key => $value) { $comments[$key] = DifferentialInlineComment::newFromModernComment( $value); } return $comments; } public function executeOne() { return head($this->execute()); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); // Only find inline comments. $where[] = qsprintf( $conn_r, 'changesetID IS NOT NULL'); if ($this->revisionIDs) { // Look up revision PHIDs. $revision_phids = queryfx_all( $conn_r, 'SELECT phid FROM %T WHERE id IN (%Ld)', id(new DifferentialRevision())->getTableName(), $this->revisionIDs); if (!$revision_phids) { throw new PhabricatorEmptyQueryException(); } $revision_phids = ipull($revision_phids, 'phid'); $where[] = qsprintf( $conn_r, 'revisionPHID IN (%Ls)', $revision_phids); } if ($this->notDraft) { $where[] = qsprintf( $conn_r, 'transactionPHID IS NOT NULL'); } if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->viewerAndChangesetIDs) { list($phid, $ids) = $this->viewerAndChangesetIDs; $where[] = qsprintf( $conn_r, 'changesetID IN (%Ld) AND ((authorPHID = %s AND isDeleted = 0) OR transactionPHID IS NOT NULL)', $ids, $phid); } if ($this->draftComments) { list($phid, $rev_id) = $this->draftComments; $rev_phid = queryfx_one( $conn_r, 'SELECT phid FROM %T WHERE id = %d', id(new DifferentialRevision())->getTableName(), $rev_id); if (!$rev_phid) { throw new PhabricatorEmptyQueryException(); } $rev_phid = $rev_phid['phid']; $where[] = qsprintf( $conn_r, 'authorPHID = %s AND revisionPHID = %s AND transactionPHID IS NULL AND isDeleted = 0', $phid, $rev_phid); } if ($this->draftsByAuthors) { $where[] = qsprintf( $conn_r, 'authorPHID IN (%Ls) AND isDeleted = 0 AND transactionPHID IS NULL', $this->draftsByAuthors); } return $this->formatWhereClause($where); } } diff --git a/src/applications/differential/query/DifferentialRevisionQuery.php b/src/applications/differential/query/DifferentialRevisionQuery.php index 13399bd7cc..63b5256454 100644 --- a/src/applications/differential/query/DifferentialRevisionQuery.php +++ b/src/applications/differential/query/DifferentialRevisionQuery.php @@ -1,1164 +1,1164 @@ withStatus(DifferentialRevisionQuery::STATUS_OPEN) * ->execute(); * * @task config Query Configuration * @task exec Query Execution * @task internal Internals */ final class DifferentialRevisionQuery extends PhabricatorCursorPagedPolicyAwareQuery { private $pathIDs = array(); private $status = 'status-any'; const STATUS_ANY = 'status-any'; const STATUS_OPEN = 'status-open'; const STATUS_ACCEPTED = 'status-accepted'; const STATUS_NEEDS_REVIEW = 'status-needs-review'; const STATUS_NEEDS_REVISION = 'status-needs-revision'; const STATUS_CLOSED = 'status-closed'; const STATUS_ABANDONED = 'status-abandoned'; private $authors = array(); private $draftAuthors = array(); private $ccs = array(); private $reviewers = array(); private $revIDs = array(); private $commitHashes = array(); private $commitPHIDs = array(); private $phids = array(); private $responsibles = array(); private $branches = array(); private $arcanistProjectPHIDs = array(); private $repositoryPHIDs; private $updatedEpochMin; private $updatedEpochMax; const ORDER_MODIFIED = 'order-modified'; const ORDER_CREATED = 'order-created'; private $needRelationships = false; private $needActiveDiffs = false; private $needDiffIDs = false; private $needCommitPHIDs = false; private $needHashes = false; private $needReviewerStatus = false; private $needReviewerAuthority; private $needDrafts; private $needFlags; private $buildingGlobalOrder; /* -( Query Configuration )------------------------------------------------ */ /** * Filter results to revisions which affect a Diffusion path ID in a given * repository. You can call this multiple times to select revisions for * several paths. * * @param int Diffusion repository ID. * @param int Diffusion path ID. * @return this * @task config */ public function withPath($repository_id, $path_id) { $this->pathIDs[] = array( 'repositoryID' => $repository_id, 'pathID' => $path_id, ); return $this; } /** * Filter results to revisions authored by one of the given PHIDs. Calling * this function will clear anything set by previous calls to * @{method:withAuthors}. * * @param array List of PHIDs of authors * @return this * @task config */ public function withAuthors(array $author_phids) { $this->authors = $author_phids; return $this; } /** * Filter results to revisions with comments authored by the given PHIDs. * * @param array List of PHIDs of authors * @return this * @task config */ public function withDraftRepliesByAuthors(array $author_phids) { $this->draftAuthors = $author_phids; return $this; } /** * Filter results to revisions which CC one of the listed people. Calling this * function will clear anything set by previous calls to @{method:withCCs}. * * @param array List of PHIDs of subscribers. * @return this * @task config */ public function withCCs(array $cc_phids) { $this->ccs = $cc_phids; return $this; } /** * Filter results to revisions that have one of the provided PHIDs as * reviewers. Calling this function will clear anything set by previous calls * to @{method:withReviewers}. * * @param array List of PHIDs of reviewers * @return this * @task config */ public function withReviewers(array $reviewer_phids) { $this->reviewers = $reviewer_phids; return $this; } /** * Filter results to revisions that have one of the provided commit hashes. * Calling this function will clear anything set by previous calls to * @{method:withCommitHashes}. * * @param array List of pairs * @return this * @task config */ public function withCommitHashes(array $commit_hashes) { $this->commitHashes = $commit_hashes; return $this; } /** * Filter results to revisions that have one of the provided PHIDs as * commits. Calling this function will clear anything set by previous calls * to @{method:withCommitPHIDs}. * * @param array List of PHIDs of commits * @return this * @task config */ public function withCommitPHIDs(array $commit_phids) { $this->commitPHIDs = $commit_phids; return $this; } /** * Filter results to revisions with a given status. Provide a class constant, * such as `DifferentialRevisionQuery::STATUS_OPEN`. * * @param const Class STATUS constant, like STATUS_OPEN. * @return this * @task config */ public function withStatus($status_constant) { $this->status = $status_constant; return $this; } /** * Filter results to revisions on given branches. * * @param list List of branch names. * @return this * @task config */ public function withBranches(array $branches) { $this->branches = $branches; return $this; } /** * Filter results to only return revisions whose ids are in the given set. * * @param array List of revision ids * @return this * @task config */ public function withIDs(array $ids) { $this->revIDs = $ids; return $this; } /** * Filter results to only return revisions whose PHIDs are in the given set. * * @param array List of revision PHIDs * @return this * @task config */ public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } /** * Given a set of users, filter results to return only revisions they are * responsible for (i.e., they are either authors or reviewers). * * @param array List of user PHIDs. * @return this * @task config */ public function withResponsibleUsers(array $responsible_phids) { $this->responsibles = $responsible_phids; return $this; } /** * Filter results to only return revisions with a given set of arcanist * projects. * * @param array List of project PHIDs. * @return this * @task config */ public function withArcanistProjectPHIDs(array $arc_project_phids) { $this->arcanistProjectPHIDs = $arc_project_phids; return $this; } public function withRepositoryPHIDs(array $repository_phids) { $this->repositoryPHIDs = $repository_phids; return $this; } public function withUpdatedEpochBetween($min, $max) { $this->updatedEpochMin = $min; $this->updatedEpochMax = $max; return $this; } /** * Set result ordering. Provide a class constant, such as * `DifferentialRevisionQuery::ORDER_CREATED`. * * @task config */ public function setOrder($order_constant) { switch ($order_constant) { case self::ORDER_CREATED: $this->setOrderVector(array('id')); break; case self::ORDER_MODIFIED: $this->setOrderVector(array('updated', 'id')); break; default: throw new Exception(pht('Unknown order "%s".', $order_constant)); } return $this; } /** * Set whether or not the query will load and attach relationships. * * @param bool True to load and attach relationships. * @return this * @task config */ public function needRelationships($need_relationships) { $this->needRelationships = $need_relationships; return $this; } /** * Set whether or not the query should load the active diff for each * revision. * * @param bool True to load and attach diffs. * @return this * @task config */ public function needActiveDiffs($need_active_diffs) { $this->needActiveDiffs = $need_active_diffs; return $this; } /** * Set whether or not the query should load the associated commit PHIDs for * each revision. * * @param bool True to load and attach diffs. * @return this * @task config */ public function needCommitPHIDs($need_commit_phids) { $this->needCommitPHIDs = $need_commit_phids; return $this; } /** * Set whether or not the query should load associated diff IDs for each * revision. * * @param bool True to load and attach diff IDs. * @return this * @task config */ public function needDiffIDs($need_diff_ids) { $this->needDiffIDs = $need_diff_ids; return $this; } /** * Set whether or not the query should load associated commit hashes for each * revision. * * @param bool True to load and attach commit hashes. * @return this * @task config */ public function needHashes($need_hashes) { $this->needHashes = $need_hashes; return $this; } /** * Set whether or not the query should load associated reviewer status. * * @param bool True to load and attach reviewers. * @return this * @task config */ public function needReviewerStatus($need_reviewer_status) { $this->needReviewerStatus = $need_reviewer_status; return $this; } /** * Request information about the viewer's authority to act on behalf of each * reviewer. In particular, they have authority to act on behalf of projects * they are a member of. * * @param bool True to load and attach authority. * @return this * @task config */ public function needReviewerAuthority($need_reviewer_authority) { $this->needReviewerAuthority = $need_reviewer_authority; return $this; } public function needFlags($need_flags) { $this->needFlags = $need_flags; return $this; } public function needDrafts($need_drafts) { $this->needDrafts = $need_drafts; return $this; } /* -( Query Execution )---------------------------------------------------- */ /** * Execute the query as configured, returning matching * @{class:DifferentialRevision} objects. * * @return list List of matching DifferentialRevision objects. * @task exec */ protected function loadPage() { $table = new DifferentialRevision(); $conn_r = $table->establishConnection('r'); $data = $this->loadData(); return $table->loadAllFromArray($data); } protected function willFilterPage(array $revisions) { $viewer = $this->getViewer(); $repository_phids = mpull($revisions, 'getRepositoryPHID'); $repository_phids = array_filter($repository_phids); $repositories = array(); if ($repository_phids) { $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getViewer()) ->withPHIDs($repository_phids) ->execute(); $repositories = mpull($repositories, null, 'getPHID'); } // If a revision is associated with a repository: // // - the viewer must be able to see the repository; or // - the viewer must have an automatic view capability. // // In the latter case, we'll load the revision but not load the repository. $can_view = PhabricatorPolicyCapability::CAN_VIEW; foreach ($revisions as $key => $revision) { $repo_phid = $revision->getRepositoryPHID(); if (!$repo_phid) { // The revision has no associated repository. Attach `null` and move on. $revision->attachRepository(null); continue; } $repository = idx($repositories, $repo_phid); if ($repository) { // The revision has an associated repository, and the viewer can see // it. Attach it and move on. $revision->attachRepository($repository); continue; } if ($revision->hasAutomaticCapability($can_view, $viewer)) { // The revision has an associated repository which the viewer can not // see, but the viewer has an automatic capability on this revision. // Load the revision without attaching a repository. $revision->attachRepository(null); continue; } if ($this->getViewer()->isOmnipotent()) { // The viewer is omnipotent. Allow the revision to load even without // a repository. $revision->attachRepository(null); continue; } // The revision has an associated repository, and the viewer can't see // it, and the viewer has no special capabilities. Filter out this // revision. $this->didRejectResult($revision); unset($revisions[$key]); } if (!$revisions) { return array(); } $table = new DifferentialRevision(); $conn_r = $table->establishConnection('r'); if ($this->needRelationships) { $this->loadRelationships($conn_r, $revisions); } if ($this->needCommitPHIDs) { $this->loadCommitPHIDs($conn_r, $revisions); } $need_active = $this->needActiveDiffs; $need_ids = $need_active || $this->needDiffIDs; if ($need_ids) { $this->loadDiffIDs($conn_r, $revisions); } if ($need_active) { $this->loadActiveDiffs($conn_r, $revisions); } if ($this->needHashes) { $this->loadHashes($conn_r, $revisions); } if ($this->needReviewerStatus || $this->needReviewerAuthority) { $this->loadReviewers($conn_r, $revisions); } return $revisions; } protected function didFilterPage(array $revisions) { $viewer = $this->getViewer(); if ($this->needFlags) { $flags = id(new PhabricatorFlagQuery()) ->setViewer($viewer) ->withOwnerPHIDs(array($viewer->getPHID())) ->withObjectPHIDs(mpull($revisions, 'getPHID')) ->execute(); $flags = mpull($flags, null, 'getObjectPHID'); foreach ($revisions as $revision) { $revision->attachFlag( $viewer, idx($flags, $revision->getPHID())); } } if ($this->needDrafts) { $drafts = id(new DifferentialDraft())->loadAllWhere( 'authorPHID = %s AND objectPHID IN (%Ls)', $viewer->getPHID(), mpull($revisions, 'getPHID')); $drafts = mgroup($drafts, 'getObjectPHID'); foreach ($revisions as $revision) { $revision->attachDrafts( $viewer, idx($drafts, $revision->getPHID(), array())); } } return $revisions; } private function loadData() { $table = new DifferentialRevision(); $conn_r = $table->establishConnection('r'); $selects = array(); // NOTE: If the query includes "responsiblePHIDs", we execute it as a // UNION of revisions they own and revisions they're reviewing. This has // much better performance than doing it with JOIN/WHERE. if ($this->responsibles) { $basic_authors = $this->authors; $basic_reviewers = $this->reviewers; $authority_projects = id(new PhabricatorProjectQuery()) ->setViewer($this->getViewer()) ->withMemberPHIDs($this->responsibles) ->execute(); $authority_phids = mpull($authority_projects, 'getPHID'); try { // Build the query where the responsible users are authors. $this->authors = array_merge($basic_authors, $this->responsibles); $this->reviewers = $basic_reviewers; $selects[] = $this->buildSelectStatement($conn_r); // Build the query where the responsible users are reviewers, or // projects they are members of are reviewers. $this->authors = $basic_authors; $this->reviewers = array_merge( $basic_reviewers, $this->responsibles, $authority_phids); $selects[] = $this->buildSelectStatement($conn_r); // Put everything back like it was. $this->authors = $basic_authors; $this->reviewers = $basic_reviewers; } catch (Exception $ex) { $this->authors = $basic_authors; $this->reviewers = $basic_reviewers; throw $ex; } } else { $selects[] = $this->buildSelectStatement($conn_r); } if (count($selects) > 1) { $this->buildingGlobalOrder = true; $query = qsprintf( $conn_r, '%Q %Q %Q', implode(' UNION DISTINCT ', $selects), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); } else { $query = head($selects); } return queryfx_all($conn_r, '%Q', $query); } private function buildSelectStatement(AphrontDatabaseConnection $conn_r) { $table = new DifferentialRevision(); $select = qsprintf( $conn_r, 'SELECT r.* FROM %T r', $table->getTableName()); $joins = $this->buildJoinsClause($conn_r); $where = $this->buildWhereClause($conn_r); $group_by = $this->buildGroupByClause($conn_r); $this->buildingGlobalOrder = false; $order_by = $this->buildOrderClause($conn_r); $limit = $this->buildLimitClause($conn_r); return qsprintf( $conn_r, '(%Q %Q %Q %Q %Q %Q)', $select, $joins, $where, $group_by, $order_by, $limit); } /* -( Internals )---------------------------------------------------------- */ /** * @task internal */ private function buildJoinsClause($conn_r) { $joins = array(); if ($this->pathIDs) { $path_table = new DifferentialAffectedPath(); $joins[] = qsprintf( $conn_r, 'JOIN %T p ON p.revisionID = r.id', $path_table->getTableName()); } if ($this->commitHashes) { $joins[] = qsprintf( $conn_r, 'JOIN %T hash_rel ON hash_rel.revisionID = r.id', ArcanistDifferentialRevisionHash::TABLE_NAME); } if ($this->ccs) { $joins[] = qsprintf( $conn_r, 'JOIN %T e_ccs ON e_ccs.src = r.phid '. 'AND e_ccs.type = %s '. 'AND e_ccs.dst in (%Ls)', PhabricatorEdgeConfig::TABLE_NAME_EDGE, PhabricatorObjectHasSubscriberEdgeType::EDGECONST, $this->ccs); } if ($this->reviewers) { $joins[] = qsprintf( $conn_r, 'JOIN %T e_reviewers ON e_reviewers.src = r.phid '. 'AND e_reviewers.type = %s '. 'AND e_reviewers.dst in (%Ls)', PhabricatorEdgeConfig::TABLE_NAME_EDGE, DifferentialRevisionHasReviewerEdgeType::EDGECONST, $this->reviewers); } if ($this->draftAuthors) { $differential_draft = new DifferentialDraft(); $joins[] = qsprintf( $conn_r, 'JOIN %T has_draft ON has_draft.objectPHID = r.phid '. 'AND has_draft.authorPHID IN (%Ls)', $differential_draft->getTableName(), $this->draftAuthors); } if ($this->commitPHIDs) { $joins[] = qsprintf( $conn_r, 'JOIN %T commits ON commits.revisionID = r.id', DifferentialRevision::TABLE_COMMIT); } $joins = implode(' ', $joins); return $joins; } /** * @task internal */ - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->pathIDs) { $path_clauses = array(); $repo_info = igroup($this->pathIDs, 'repositoryID'); foreach ($repo_info as $repository_id => $paths) { $path_clauses[] = qsprintf( $conn_r, '(p.repositoryID = %d AND p.pathID IN (%Ld))', $repository_id, ipull($paths, 'pathID')); } $path_clauses = '('.implode(' OR ', $path_clauses).')'; $where[] = $path_clauses; } if ($this->authors) { $where[] = qsprintf( $conn_r, 'r.authorPHID IN (%Ls)', $this->authors); } if ($this->revIDs) { $where[] = qsprintf( $conn_r, 'r.id IN (%Ld)', $this->revIDs); } if ($this->repositoryPHIDs) { $where[] = qsprintf( $conn_r, 'r.repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } if ($this->commitHashes) { $hash_clauses = array(); foreach ($this->commitHashes as $info) { list($type, $hash) = $info; $hash_clauses[] = qsprintf( $conn_r, '(hash_rel.type = %s AND hash_rel.hash = %s)', $type, $hash); } $hash_clauses = '('.implode(' OR ', $hash_clauses).')'; $where[] = $hash_clauses; } if ($this->commitPHIDs) { $where[] = qsprintf( $conn_r, 'commits.commitPHID IN (%Ls)', $this->commitPHIDs); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'r.phid IN (%Ls)', $this->phids); } if ($this->branches) { $where[] = qsprintf( $conn_r, 'r.branchName in (%Ls)', $this->branches); } if ($this->arcanistProjectPHIDs) { $where[] = qsprintf( $conn_r, 'r.arcanistProjectPHID in (%Ls)', $this->arcanistProjectPHIDs); } if ($this->updatedEpochMin !== null) { $where[] = qsprintf( $conn_r, 'r.dateModified >= %d', $this->updatedEpochMin); } if ($this->updatedEpochMax !== null) { $where[] = qsprintf( $conn_r, 'r.dateModified <= %d', $this->updatedEpochMax); } switch ($this->status) { case self::STATUS_ANY: break; case self::STATUS_OPEN: $where[] = qsprintf( $conn_r, 'r.status IN (%Ld)', DifferentialRevisionStatus::getOpenStatuses()); break; case self::STATUS_NEEDS_REVIEW: $where[] = qsprintf( $conn_r, 'r.status IN (%Ld)', array( ArcanistDifferentialRevisionStatus::NEEDS_REVIEW, )); break; case self::STATUS_NEEDS_REVISION: $where[] = qsprintf( $conn_r, 'r.status IN (%Ld)', array( ArcanistDifferentialRevisionStatus::NEEDS_REVISION, )); break; case self::STATUS_ACCEPTED: $where[] = qsprintf( $conn_r, 'r.status IN (%Ld)', array( ArcanistDifferentialRevisionStatus::ACCEPTED, )); break; case self::STATUS_CLOSED: $where[] = qsprintf( $conn_r, 'r.status IN (%Ld)', DifferentialRevisionStatus::getClosedStatuses()); break; case self::STATUS_ABANDONED: $where[] = qsprintf( $conn_r, 'r.status IN (%Ld)', array( ArcanistDifferentialRevisionStatus::ABANDONED, )); break; default: throw new Exception( "Unknown revision status filter constant '{$this->status}'!"); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } /** * @task internal */ private function buildGroupByClause($conn_r) { $join_triggers = array_merge( $this->pathIDs, $this->ccs, $this->reviewers); $needs_distinct = (count($join_triggers) > 1); if ($needs_distinct) { return 'GROUP BY r.id'; } else { return ''; } } protected function getDefaultOrderVector() { return array('updated', 'id'); } public function getOrderableColumns() { $primary = ($this->buildingGlobalOrder ? null : 'r'); return array( 'id' => array( 'table' => $primary, 'column' => 'id', 'type' => 'int', 'unique' => true, ), 'updated' => array( 'table' => $primary, 'column' => 'dateModified', 'type' => 'int', ), ); } protected function getPagingValueMap($cursor, array $keys) { $revision = $this->loadCursorObject($cursor); return array( 'id' => $revision->getID(), 'updated' => $revision->getDateModified(), ); } private function loadRelationships($conn_r, array $revisions) { assert_instances_of($revisions, 'DifferentialRevision'); $type_reviewer = DifferentialRevisionHasReviewerEdgeType::EDGECONST; $type_subscriber = PhabricatorObjectHasSubscriberEdgeType::EDGECONST; $edges = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(mpull($revisions, 'getPHID')) ->withEdgeTypes(array($type_reviewer, $type_subscriber)) ->setOrder(PhabricatorEdgeQuery::ORDER_OLDEST_FIRST) ->execute(); $type_map = array( DifferentialRevision::RELATION_REVIEWER => $type_reviewer, DifferentialRevision::RELATION_SUBSCRIBED => $type_subscriber, ); foreach ($revisions as $revision) { $data = array(); foreach ($type_map as $rel_type => $edge_type) { $revision_edges = $edges[$revision->getPHID()][$edge_type]; foreach ($revision_edges as $dst_phid => $edge_data) { $data[] = array( 'relation' => $rel_type, 'objectPHID' => $dst_phid, 'reasonPHID' => null, ); } } $revision->attachRelationships($data); } } private function loadCommitPHIDs($conn_r, array $revisions) { assert_instances_of($revisions, 'DifferentialRevision'); $commit_phids = queryfx_all( $conn_r, 'SELECT * FROM %T WHERE revisionID IN (%Ld)', DifferentialRevision::TABLE_COMMIT, mpull($revisions, 'getID')); $commit_phids = igroup($commit_phids, 'revisionID'); foreach ($revisions as $revision) { $phids = idx($commit_phids, $revision->getID(), array()); $phids = ipull($phids, 'commitPHID'); $revision->attachCommitPHIDs($phids); } } private function loadDiffIDs($conn_r, array $revisions) { assert_instances_of($revisions, 'DifferentialRevision'); $diff_table = new DifferentialDiff(); $diff_ids = queryfx_all( $conn_r, 'SELECT revisionID, id FROM %T WHERE revisionID IN (%Ld) ORDER BY id DESC', $diff_table->getTableName(), mpull($revisions, 'getID')); $diff_ids = igroup($diff_ids, 'revisionID'); foreach ($revisions as $revision) { $ids = idx($diff_ids, $revision->getID(), array()); $ids = ipull($ids, 'id'); $revision->attachDiffIDs($ids); } } private function loadActiveDiffs($conn_r, array $revisions) { assert_instances_of($revisions, 'DifferentialRevision'); $diff_table = new DifferentialDiff(); $load_ids = array(); foreach ($revisions as $revision) { $diffs = $revision->getDiffIDs(); if ($diffs) { $load_ids[] = max($diffs); } } $active_diffs = array(); if ($load_ids) { $active_diffs = $diff_table->loadAllWhere( 'id IN (%Ld)', $load_ids); } $active_diffs = mpull($active_diffs, null, 'getRevisionID'); foreach ($revisions as $revision) { $revision->attachActiveDiff(idx($active_diffs, $revision->getID())); } } private function loadHashes( AphrontDatabaseConnection $conn_r, array $revisions) { assert_instances_of($revisions, 'DifferentialRevision'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T WHERE revisionID IN (%Ld)', 'differential_revisionhash', mpull($revisions, 'getID')); $data = igroup($data, 'revisionID'); foreach ($revisions as $revision) { $hashes = idx($data, $revision->getID(), array()); $list = array(); foreach ($hashes as $hash) { $list[] = array($hash['type'], $hash['hash']); } $revision->attachHashes($list); } } private function loadReviewers( AphrontDatabaseConnection $conn_r, array $revisions) { assert_instances_of($revisions, 'DifferentialRevision'); $edge_type = DifferentialRevisionHasReviewerEdgeType::EDGECONST; $edges = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(mpull($revisions, 'getPHID')) ->withEdgeTypes(array($edge_type)) ->needEdgeData(true) ->setOrder(PhabricatorEdgeQuery::ORDER_OLDEST_FIRST) ->execute(); $viewer = $this->getViewer(); $viewer_phid = $viewer->getPHID(); $allow_key = 'differential.allow-self-accept'; $allow_self = PhabricatorEnv::getEnvConfig($allow_key); // Figure out which of these reviewers the viewer has authority to act as. if ($this->needReviewerAuthority && $viewer_phid) { $authority = $this->loadReviewerAuthority( $revisions, $edges, $allow_self); } foreach ($revisions as $revision) { $revision_edges = $edges[$revision->getPHID()][$edge_type]; $reviewers = array(); foreach ($revision_edges as $reviewer_phid => $edge) { $reviewer = new DifferentialReviewer($reviewer_phid, $edge['data']); if ($this->needReviewerAuthority) { if (!$viewer_phid) { // Logged-out users never have authority. $has_authority = false; } else if ((!$allow_self) && ($revision->getAuthorPHID() == $viewer_phid)) { // The author can never have authority unless we allow self-accept. $has_authority = false; } else { // Otherwise, look up whether th viewer has authority. $has_authority = isset($authority[$reviewer_phid]); } $reviewer->attachAuthority($viewer, $has_authority); } $reviewers[$reviewer_phid] = $reviewer; } $revision->attachReviewerStatus($reviewers); } } public static function splitResponsible(array $revisions, array $user_phids) { $blocking = array(); $active = array(); $waiting = array(); $status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW; // Bucket revisions into $blocking (revisions where you are blocking // others), $active (revisions you need to do something about) and $waiting // (revisions you're waiting on someone else to do something about). foreach ($revisions as $revision) { $needs_review = ($revision->getStatus() == $status_review); $filter_is_author = in_array($revision->getAuthorPHID(), $user_phids); if (!$revision->getReviewers()) { $needs_review = false; $author_is_reviewer = false; } else { $author_is_reviewer = in_array( $revision->getAuthorPHID(), $revision->getReviewers()); } // If exactly one of "needs review" and "the user is the author" is // true, the user needs to act on it. Otherwise, they're waiting on // it. if ($needs_review ^ $filter_is_author) { if ($needs_review) { array_unshift($blocking, $revision); } else { $active[] = $revision; } // User is author **and** reviewer. An exotic but configurable workflow. // User needs to act on it double. } else if ($needs_review && $author_is_reviewer) { array_unshift($blocking, $revision); $active[] = $revision; } else { $waiting[] = $revision; } } return array($blocking, $active, $waiting); } private function loadReviewerAuthority( array $revisions, array $edges, $allow_self) { $revision_map = mpull($revisions, null, 'getPHID'); $viewer_phid = $this->getViewer()->getPHID(); // Find all the project reviewers which the user may have authority over. $project_phids = array(); $project_type = PhabricatorProjectProjectPHIDType::TYPECONST; $edge_type = DifferentialRevisionHasReviewerEdgeType::EDGECONST; foreach ($edges as $src => $types) { if (!$allow_self) { if ($revision_map[$src]->getAuthorPHID() == $viewer_phid) { // If self-review isn't permitted, the user will never have // authority over projects on revisions they authored because you // can't accept your own revisions, so we don't need to load any // data about these reviewers. continue; } } $edge_data = idx($types, $edge_type, array()); foreach ($edge_data as $dst => $data) { if (phid_get_type($dst) == $project_type) { $project_phids[] = $dst; } } } // Now, figure out which of these projects the viewer is actually a // member of. $project_authority = array(); if ($project_phids) { $project_authority = id(new PhabricatorProjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($project_phids) ->withMemberPHIDs(array($viewer_phid)) ->execute(); $project_authority = mpull($project_authority, 'getPHID'); } // Finally, the viewer has authority over themselves. return array( $viewer_phid => true, ) + array_fuse($project_authority); } public function getQueryApplicationClass() { return 'PhabricatorDifferentialApplication'; } } diff --git a/src/applications/diffusion/query/DiffusionCommitQuery.php b/src/applications/diffusion/query/DiffusionCommitQuery.php index 22331e8510..8fd9d0c73e 100644 --- a/src/applications/diffusion/query/DiffusionCommitQuery.php +++ b/src/applications/diffusion/query/DiffusionCommitQuery.php @@ -1,566 +1,566 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs(array $phids) { $this->authorPHIDs = $phids; return $this; } /** * Load commits by partial or full identifiers, e.g. "rXab82393", "rX1234", * or "a9caf12". When an identifier matches multiple commits, they will all * be returned; callers should be prepared to deal with more results than * they queried for. */ public function withIdentifiers(array $identifiers) { $this->identifiers = $identifiers; return $this; } /** * Look up commits in a specific repository. This is a shorthand for calling * @{method:withDefaultRepository} and @{method:withRepositoryIDs}. */ public function withRepository(PhabricatorRepository $repository) { $this->withDefaultRepository($repository); $this->withRepositoryIDs(array($repository->getID())); return $this; } /** * Look up commits in a specific repository. Prefer * @{method:withRepositoryIDs}; the underyling table is keyed by ID such * that this method requires a separate initial query to map PHID to ID. */ public function withRepositoryPHIDs(array $phids) { $this->repositoryPHIDs = $phids; } /** * If a default repository is provided, ambiguous commit identifiers will * be assumed to belong to the default repository. * * For example, "r123" appearing in a commit message in repository X is * likely to be unambiguously "rX123". Normally the reference would be * considered ambiguous, but if you provide a default repository it will * be correctly resolved. */ public function withDefaultRepository(PhabricatorRepository $repository) { $this->defaultRepository = $repository; return $this; } public function withRepositoryIDs(array $repository_ids) { $this->repositoryIDs = $repository_ids; return $this; } public function needCommitData($need) { $this->needCommitData = $need; return $this; } public function needAuditRequests($need) { $this->needAuditRequests = $need; return $this; } /** * Returns true if we should join the audit table, either because we're * interested in the information if it's available or because matching rows * must always have it. */ private function shouldJoinAudits() { return $this->auditStatus || $this->rowsMustHaveAudits(); } /** * Return true if we should `JOIN` (vs `LEFT JOIN`) the audit table, because * matching commits will always have audit rows. */ private function rowsMustHaveAudits() { return $this->auditIDs || $this->auditorPHIDs || $this->auditAwaitingUser; } public function withAuditIDs(array $ids) { $this->auditIDs = $ids; return $this; } public function withAuditorPHIDs(array $auditor_phids) { $this->auditorPHIDs = $auditor_phids; return $this; } public function withAuditAwaitingUser(PhabricatorUser $user) { $this->auditAwaitingUser = $user; return $this; } public function withAuditStatus($status) { $this->auditStatus = $status; return $this; } public function getIdentifierMap() { if ($this->identifierMap === null) { throw new Exception( 'You must execute() the query before accessing the identifier map.'); } return $this->identifierMap; } protected function getPrimaryTableAlias() { return 'commit'; } protected function willExecute() { if ($this->identifierMap === null) { $this->identifierMap = array(); } } protected function loadPage() { $table = new PhabricatorRepositoryCommit(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT commit.* FROM %T commit %Q %Q %Q %Q %Q', $table->getTableName(), $this->buildJoinClause($conn_r), $this->buildWhereClause($conn_r), $this->buildGroupClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $commits) { $repository_ids = mpull($commits, 'getRepositoryID', 'getRepositoryID'); $repos = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getViewer()) ->withIDs($repository_ids) ->execute(); $min_qualified = PhabricatorRepository::MINIMUM_QUALIFIED_HASH; $result = array(); foreach ($commits as $key => $commit) { $repo = idx($repos, $commit->getRepositoryID()); if ($repo) { $commit->attachRepository($repo); } else { unset($commits[$key]); continue; } // Build the identifierMap if ($this->identifiers !== null) { $ids = array_fuse($this->identifiers); $prefixes = array( 'r'.$commit->getRepository()->getCallsign(), 'r'.$commit->getRepository()->getCallsign().':', 'R'.$commit->getRepository()->getID().':', '', // No prefix is valid too and will only match the commitIdentifier ); $suffix = $commit->getCommitIdentifier(); if ($commit->getRepository()->isSVN()) { foreach ($prefixes as $prefix) { if (isset($ids[$prefix.$suffix])) { $result[$prefix.$suffix][] = $commit; } } } else { // This awkward construction is so we can link the commits up in O(N) // time instead of O(N^2). for ($ii = $min_qualified; $ii <= strlen($suffix); $ii++) { $part = substr($suffix, 0, $ii); foreach ($prefixes as $prefix) { if (isset($ids[$prefix.$part])) { $result[$prefix.$part][] = $commit; } } } } } } if ($result) { foreach ($result as $identifier => $matching_commits) { if (count($matching_commits) == 1) { $result[$identifier] = head($matching_commits); } else { // This reference is ambiguous (it matches more than one commit) so // don't link it. unset($result[$identifier]); } } $this->identifierMap += $result; } return $commits; } protected function didFilterPage(array $commits) { if ($this->needCommitData) { $data = id(new PhabricatorRepositoryCommitData())->loadAllWhere( 'commitID in (%Ld)', mpull($commits, 'getID')); $data = mpull($data, null, 'getCommitID'); foreach ($commits as $commit) { $commit_data = idx($data, $commit->getID()); if (!$commit_data) { $commit_data = new PhabricatorRepositoryCommitData(); } $commit->attachCommitData($commit_data); } } // TODO: This should just be `needAuditRequests`, not `shouldJoinAudits()`, // but leave that for a future diff. if ($this->needAuditRequests || $this->shouldJoinAudits()) { $requests = id(new PhabricatorRepositoryAuditRequest())->loadAllWhere( 'commitPHID IN (%Ls)', mpull($commits, 'getPHID')); $requests = mgroup($requests, 'getCommitPHID'); foreach ($commits as $commit) { $audit_requests = idx($requests, $commit->getPHID(), array()); $commit->attachAudits($audit_requests); foreach ($audit_requests as $audit_request) { $audit_request->attachCommit($commit); } } } return $commits; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->repositoryPHIDs !== null) { $map_repositories = id (new PhabricatorRepositoryQuery()) ->setViewer($this->getViewer()) ->withPHIDs($this->repositoryPHIDs) ->execute(); if (!$map_repositories) { throw new PhabricatorEmptyQueryException(); } $repository_ids = mpull($map_repositories, 'getID'); if ($this->repositoryIDs !== null) { $repository_ids = array_merge($repository_ids, $this->repositoryIDs); } $this->withRepositoryIDs($repository_ids); } if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'commit.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'commit.phid IN (%Ls)', $this->phids); } if ($this->repositoryIDs !== null) { $where[] = qsprintf( $conn_r, 'commit.repositoryID IN (%Ld)', $this->repositoryIDs); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn_r, 'commit.authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->identifiers !== null) { $min_unqualified = PhabricatorRepository::MINIMUM_UNQUALIFIED_HASH; $min_qualified = PhabricatorRepository::MINIMUM_QUALIFIED_HASH; $refs = array(); $bare = array(); foreach ($this->identifiers as $identifier) { $matches = null; preg_match('/^(?:[rR]([A-Z]+:?|[0-9]+:))?(.*)$/', $identifier, $matches); $repo = nonempty(rtrim($matches[1], ':'), null); $commit_identifier = nonempty($matches[2], null); if ($repo === null) { if ($this->defaultRepository) { $repo = $this->defaultRepository->getCallsign(); } } if ($repo === null) { if (strlen($commit_identifier) < $min_unqualified) { continue; } $bare[] = $commit_identifier; } else { $refs[] = array( 'callsign' => $repo, 'identifier' => $commit_identifier, ); } } $sql = array(); foreach ($bare as $identifier) { $sql[] = qsprintf( $conn_r, '(commit.commitIdentifier LIKE %> AND '. 'LENGTH(commit.commitIdentifier) = 40)', $identifier); } if ($refs) { $callsigns = ipull($refs, 'callsign'); $repos = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getViewer()) ->withIdentifiers($callsigns); $repos->execute(); $repos = $repos->getIdentifierMap(); foreach ($refs as $key => $ref) { $repo = idx($repos, $ref['callsign']); if (!$repo) { continue; } if ($repo->isSVN()) { if (!ctype_digit($ref['identifier'])) { continue; } $sql[] = qsprintf( $conn_r, '(commit.repositoryID = %d AND commit.commitIdentifier = %s)', $repo->getID(), // NOTE: Because the 'commitIdentifier' column is a string, MySQL // ignores the index if we hand it an integer. Hand it a string. // See T3377. (int)$ref['identifier']); } else { if (strlen($ref['identifier']) < $min_qualified) { continue; } $sql[] = qsprintf( $conn_r, '(commit.repositoryID = %d AND commit.commitIdentifier LIKE %>)', $repo->getID(), $ref['identifier']); } } } if (!$sql) { // If we discarded all possible identifiers (e.g., they all referenced // bogus repositories or were all too short), make sure the query finds // nothing. throw new PhabricatorEmptyQueryException( pht('No commit identifiers.')); } $where[] = '('.implode(' OR ', $sql).')'; } if ($this->auditIDs !== null) { $where[] = qsprintf( $conn_r, 'audit.id IN (%Ld)', $this->auditIDs); } if ($this->auditorPHIDs !== null) { $where[] = qsprintf( $conn_r, 'audit.auditorPHID IN (%Ls)', $this->auditorPHIDs); } if ($this->auditAwaitingUser) { $awaiting_user_phid = $this->auditAwaitingUser->getPHID(); // Exclude package and project audits associated with commits where // the user is the author. $where[] = qsprintf( $conn_r, '(commit.authorPHID IS NULL OR commit.authorPHID != %s) OR (audit.auditorPHID = %s)', $awaiting_user_phid, $awaiting_user_phid); } $status = $this->auditStatus; if ($status !== null) { switch ($status) { case self::AUDIT_STATUS_PARTIAL: $where[] = qsprintf( $conn_r, 'commit.auditStatus = %d', PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED); break; case self::AUDIT_STATUS_ACCEPTED: $where[] = qsprintf( $conn_r, 'commit.auditStatus = %d', PhabricatorAuditCommitStatusConstants::FULLY_AUDITED); break; case self::AUDIT_STATUS_CONCERN: $where[] = qsprintf( $conn_r, 'audit.auditStatus = %s', PhabricatorAuditStatusConstants::CONCERNED); break; case self::AUDIT_STATUS_OPEN: $where[] = qsprintf( $conn_r, 'audit.auditStatus in (%Ls)', PhabricatorAuditStatusConstants::getOpenStatusConstants()); if ($this->auditAwaitingUser) { $where[] = qsprintf( $conn_r, 'awaiting.auditStatus IS NULL OR awaiting.auditStatus != %s', PhabricatorAuditStatusConstants::RESIGNED); } break; case self::AUDIT_STATUS_ANY: break; default: $valid = array( self::AUDIT_STATUS_ANY, self::AUDIT_STATUS_OPEN, self::AUDIT_STATUS_CONCERN, self::AUDIT_STATUS_ACCEPTED, self::AUDIT_STATUS_PARTIAL, ); throw new Exception( "Unknown audit status '{$status}'! Valid statuses are: ". implode(', ', $valid)); } } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } protected function didFilterResults(array $filtered) { if ($this->identifierMap) { foreach ($this->identifierMap as $name => $commit) { if (isset($filtered[$commit->getPHID()])) { unset($this->identifierMap[$name]); } } } } private function buildJoinClause($conn_r) { $joins = array(); $audit_request = new PhabricatorRepositoryAuditRequest(); if ($this->shouldJoinAudits()) { $joins[] = qsprintf( $conn_r, '%Q %T audit ON commit.phid = audit.commitPHID', ($this->rowsMustHaveAudits() ? 'JOIN' : 'LEFT JOIN'), $audit_request->getTableName()); } if ($this->auditAwaitingUser) { // Join the request table on the awaiting user's requests, so we can // filter out package and project requests which the user has resigned // from. $joins[] = qsprintf( $conn_r, 'LEFT JOIN %T awaiting ON audit.commitPHID = awaiting.commitPHID AND awaiting.auditorPHID = %s', $audit_request->getTableName(), $this->auditAwaitingUser->getPHID()); } if ($joins) { return implode(' ', $joins); } else { return ''; } } private function buildGroupClause(AphrontDatabaseConnection $conn_r) { $should_group = $this->shouldJoinAudits(); // TODO: Currently, the audit table is missing a unique key, so we may // require a GROUP BY if we perform this join. See T1768. This can be // removed once the table has the key. if ($this->auditAwaitingUser) { $should_group = true; } if ($should_group) { return 'GROUP BY commit.id'; } else { return ''; } } public function getQueryApplicationClass() { return 'PhabricatorDiffusionApplication'; } } diff --git a/src/applications/diffusion/query/DiffusionLintCountQuery.php b/src/applications/diffusion/query/DiffusionLintCountQuery.php index 89fca4cded..f85505af76 100644 --- a/src/applications/diffusion/query/DiffusionLintCountQuery.php +++ b/src/applications/diffusion/query/DiffusionLintCountQuery.php @@ -1,120 +1,123 @@ branchIDs = $branch_ids; return $this; } public function withPaths(array $paths) { $this->paths = $paths; return $this; } public function withCodes(array $codes) { $this->codes = $codes; return $this; } public function execute() { if (!$this->paths) { throw new Exception(pht('Call withPaths() before execute()!')); } if (!$this->branchIDs) { throw new Exception(pht('Call withBranchIDs() before execute()!')); } $conn_r = id(new PhabricatorRepositoryCommit())->establishConnection('r'); $this->paths = array_unique($this->paths); list($dirs, $paths) = $this->processPaths(); $parts = array(); foreach ($dirs as $dir) { $parts[$dir] = qsprintf( $conn_r, 'path LIKE %>', $dir); } foreach ($paths as $path) { $parts[$path] = qsprintf( $conn_r, 'path = %s', $path); } $queries = array(); foreach ($parts as $key => $part) { $queries[] = qsprintf( $conn_r, 'SELECT %s path_prefix, COUNT(*) N FROM %T %Q', $key, PhabricatorRepository::TABLE_LINTMESSAGE, - $this->buildWhereClause($conn_r, $part)); + $this->buildCustomWhereClause($conn_r, $part)); } $huge_union_query = '('.implode(') UNION ALL (', $queries).')'; $data = queryfx_all( $conn_r, '%Q', $huge_union_query); return $this->processResults($data); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r, $part) { + protected function buildCustomWhereClause( + AphrontDatabaseConnection $conn_r, + $part) { + $where = array(); $where[] = $part; if ($this->codes !== null) { $where[] = qsprintf( $conn_r, 'code IN (%Ls)', $this->codes); } if ($this->branchIDs !== null) { $where[] = qsprintf( $conn_r, 'branchID IN (%Ld)', $this->branchIDs); } return $this->formatWhereClause($where); } private function processPaths() { $dirs = array(); $paths = array(); foreach ($this->paths as $path) { $path = '/'.$path; if (substr($path, -1) == '/') { $dirs[] = $path; } else { $paths[] = $path; } } return array($dirs, $paths); } private function processResults(array $data) { $data = ipull($data, 'N', 'path_prefix'); // Strip the leading "/" back off each path. $output = array(); foreach ($data as $path => $count) { $output[substr($path, 1)] = $count; } return $output; } } diff --git a/src/applications/diffusion/query/DiffusionPathQuery.php b/src/applications/diffusion/query/DiffusionPathQuery.php index 33e7b57909..f442da7173 100644 --- a/src/applications/diffusion/query/DiffusionPathQuery.php +++ b/src/applications/diffusion/query/DiffusionPathQuery.php @@ -1,43 +1,43 @@ pathIDs = $path_ids; return $this; } public function execute() { $conn_r = id(new PhabricatorRepository())->establishConnection('r'); $where = $this->buildWhereClause($conn_r); $results = queryfx_all( $conn_r, 'SELECT * FROM %T %Q', PhabricatorRepository::TABLE_PATH, $where); return ipull($results, null, 'id'); } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->pathIDs) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->pathIDs); } if ($where) { return 'WHERE ('.implode(') AND (', $where).')'; } else { return ''; } } } diff --git a/src/applications/diffusion/query/DiffusionSymbolQuery.php b/src/applications/diffusion/query/DiffusionSymbolQuery.php index e5cb2b91c4..053f4a222e 100644 --- a/src/applications/diffusion/query/DiffusionSymbolQuery.php +++ b/src/applications/diffusion/query/DiffusionSymbolQuery.php @@ -1,299 +1,299 @@ viewer = $viewer; return $this; } /** * @task config */ public function getViewer() { return $this->viewer; } /** * @task config */ public function setContext($context) { $this->context = $context; return $this; } /** * @task config */ public function setName($name) { $this->name = $name; return $this; } /** * @task config */ public function setNamePrefix($name_prefix) { $this->namePrefix = $name_prefix; return $this; } /** * @task config */ public function setProjectIDs(array $project_ids) { $this->projectIDs = $project_ids; return $this; } /** * @task config */ public function setLanguage($language) { $this->language = $language; return $this; } /** * @task config */ public function setType($type) { $this->type = $type; return $this; } /** * @task config */ public function needPaths($need_paths) { $this->needPaths = $need_paths; return $this; } /** * @task config */ public function needArcanistProjects($need_arcanist_projects) { $this->needArcanistProjects = $need_arcanist_projects; return $this; } /** * @task config */ public function needRepositories($need_repositories) { $this->needRepositories = $need_repositories; return $this; } /* -( Executing the Query )------------------------------------------------ */ /** * @task exec */ public function execute() { if ($this->name && $this->namePrefix) { throw new Exception( 'You can not set both a name and a name prefix!'); } else if (!$this->name && !$this->namePrefix) { throw new Exception( 'You must set a name or a name prefix!'); } $symbol = new PhabricatorRepositorySymbol(); $conn_r = $symbol->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $symbol->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $symbols = $symbol->loadAllFromArray($data); if ($symbols) { if ($this->needPaths) { $this->loadPaths($symbols); } if ($this->needArcanistProjects || $this->needRepositories) { $this->loadArcanistProjects($symbols); } if ($this->needRepositories) { $this->loadRepositories($symbols); } } return $symbols; } /* -( Internals )---------------------------------------------------------- */ /** * @task internal */ private function buildOrderClause($conn_r) { return qsprintf( $conn_r, 'ORDER BY symbolName ASC'); } /** * @task internal */ - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if (isset($this->context)) { $where[] = qsprintf( $conn_r, 'symbolContext = %s', $this->context); } if ($this->name) { $where[] = qsprintf( $conn_r, 'symbolName = %s', $this->name); } if ($this->namePrefix) { $where[] = qsprintf( $conn_r, 'symbolName LIKE %>', $this->namePrefix); } if ($this->projectIDs) { $where[] = qsprintf( $conn_r, 'arcanistProjectID IN (%Ld)', $this->projectIDs); } if ($this->language) { $where[] = qsprintf( $conn_r, 'symbolLanguage = %s', $this->language); } if ($this->type) { $where[] = qsprintf( $conn_r, 'symbolType = %s', $this->type); } return $this->formatWhereClause($where); } /** * @task internal */ private function loadPaths(array $symbols) { assert_instances_of($symbols, 'PhabricatorRepositorySymbol'); $path_map = queryfx_all( id(new PhabricatorRepository())->establishConnection('r'), 'SELECT * FROM %T WHERE id IN (%Ld)', PhabricatorRepository::TABLE_PATH, mpull($symbols, 'getPathID')); $path_map = ipull($path_map, 'path', 'id'); foreach ($symbols as $symbol) { $symbol->attachPath(idx($path_map, $symbol->getPathID())); } } /** * @task internal */ private function loadArcanistProjects(array $symbols) { assert_instances_of($symbols, 'PhabricatorRepositorySymbol'); $projects = id(new PhabricatorRepositoryArcanistProject())->loadAllWhere( 'id IN (%Ld)', mpull($symbols, 'getArcanistProjectID')); foreach ($symbols as $symbol) { $project = idx($projects, $symbol->getArcanistProjectID()); $symbol->attachArcanistProject($project); } } /** * @task internal */ private function loadRepositories(array $symbols) { assert_instances_of($symbols, 'PhabricatorRepositorySymbol'); $projects = mpull($symbols, 'getArcanistProject'); $projects = array_filter($projects); $repo_ids = mpull($projects, 'getRepositoryID'); $repo_ids = array_filter($repo_ids); if ($repo_ids) { $repos = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getViewer()) ->withIDs($repo_ids) ->execute(); } else { $repos = array(); } foreach ($symbols as $symbol) { $proj = $symbol->getArcanistProject(); if ($proj) { $symbol->attachRepository(idx($repos, $proj->getRepositoryID())); } else { $symbol->attachRepository(null); } } } } diff --git a/src/applications/diviner/query/DivinerAtomQuery.php b/src/applications/diviner/query/DivinerAtomQuery.php index 5b9b8814f7..70759a2cc1 100644 --- a/src/applications/diviner/query/DivinerAtomQuery.php +++ b/src/applications/diviner/query/DivinerAtomQuery.php @@ -1,447 +1,447 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withBookPHIDs(array $phids) { $this->bookPHIDs = $phids; return $this; } public function withTypes(array $types) { $this->types = $types; return $this; } public function withNames(array $names) { $this->names = $names; return $this; } public function withContexts(array $contexts) { $this->contexts = $contexts; return $this; } public function withIndexes(array $indexes) { $this->indexes = $indexes; return $this; } public function withNodeHashes(array $hashes) { $this->nodeHashes = $hashes; return $this; } public function withTitles($titles) { $this->titles = $titles; return $this; } public function withNameContains($text) { $this->nameContains = $text; return $this; } public function needAtoms($need) { $this->needAtoms = $need; return $this; } public function needChildren($need) { $this->needChildren = $need; return $this; } /** * Include "ghosts", which are symbols which used to exist but do not exist * currently (for example, a function which existed in an older version of * the codebase but was deleted). * * These symbols had PHIDs assigned to them, and may have other sorts of * metadata that we don't want to lose (like comments or flags), so we don't * delete them outright. They might also come back in the future: the change * which deleted the symbol might be reverted, or the documentation might * have been generated incorrectly by accident. In these cases, we can * restore the original data. * * However, most callers are not interested in these symbols, so they are * excluded by default. You can use this method to include them in results. * * @param bool True to include ghosts. * @return this */ public function withIncludeGhosts($include) { $this->includeGhosts = $include; return $this; } public function needExtends($need) { $this->needExtends = $need; return $this; } public function withIncludeUndocumentable($include) { $this->includeUndocumentable = $include; return $this; } protected function loadPage() { $table = new DivinerLiveSymbol(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $atoms) { $books = array_unique(mpull($atoms, 'getBookPHID')); $books = id(new DivinerBookQuery()) ->setViewer($this->getViewer()) ->withPHIDs($books) ->execute(); $books = mpull($books, null, 'getPHID'); foreach ($atoms as $key => $atom) { $book = idx($books, $atom->getBookPHID()); if (!$book) { unset($atoms[$key]); continue; } $atom->attachBook($book); } if ($this->needAtoms) { $atom_data = id(new DivinerLiveAtom())->loadAllWhere( 'symbolPHID IN (%Ls)', mpull($atoms, 'getPHID')); $atom_data = mpull($atom_data, null, 'getSymbolPHID'); foreach ($atoms as $key => $atom) { $data = idx($atom_data, $atom->getPHID()); if (!$data) { unset($atoms[$key]); continue; } $atom->attachAtom($data); } } // Load all of the symbols this symbol extends, recursively. Commonly, // this means all the ancestor classes and interfaces it extends and // implements. if ($this->needExtends) { // First, load all the matching symbols by name. This does 99% of the // work in most cases, assuming things are named at all reasonably. $names = array(); foreach ($atoms as $atom) { foreach ($atom->getAtom()->getExtends() as $xref) { $names[] = $xref->getName(); } } if ($names) { $xatoms = id(new DivinerAtomQuery()) ->setViewer($this->getViewer()) ->withNames($names) ->needExtends(true) ->needAtoms(true) ->needChildren($this->needChildren) ->execute(); $xatoms = mgroup($xatoms, 'getName', 'getType', 'getBookPHID'); } else { $xatoms = array(); } foreach ($atoms as $atom) { $alang = $atom->getAtom()->getLanguage(); $extends = array(); foreach ($atom->getAtom()->getExtends() as $xref) { // If there are no symbols of the matching name and type, we can't // resolve this. if (empty($xatoms[$xref->getName()][$xref->getType()])) { continue; } // If we found matches in the same documentation book, prefer them // over other matches. Otherwise, look at all the the matches. $matches = $xatoms[$xref->getName()][$xref->getType()]; if (isset($matches[$atom->getBookPHID()])) { $maybe = $matches[$atom->getBookPHID()]; } else { $maybe = array_mergev($matches); } if (!$maybe) { continue; } // Filter out matches in a different language, since, e.g., PHP // classes can not implement JS classes. $same_lang = array(); foreach ($maybe as $xatom) { if ($xatom->getAtom()->getLanguage() == $alang) { $same_lang[] = $xatom; } } if (!$same_lang) { continue; } // If we have duplicates remaining, just pick the first one. There's // nothing more we can do to figure out which is the real one. $extends[] = head($same_lang); } $atom->attachExtends($extends); } } if ($this->needChildren) { $child_hashes = $this->getAllChildHashes($atoms, $this->needExtends); if ($child_hashes) { $children = id(new DivinerAtomQuery()) ->setViewer($this->getViewer()) ->withIncludeUndocumentable(true) ->withNodeHashes($child_hashes) ->needAtoms($this->needAtoms) ->execute(); $children = mpull($children, null, 'getNodeHash'); } else { $children = array(); } $this->attachAllChildren($atoms, $children, $this->needExtends); } return $atoms; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->bookPHIDs) { $where[] = qsprintf( $conn_r, 'bookPHID IN (%Ls)', $this->bookPHIDs); } if ($this->types) { $where[] = qsprintf( $conn_r, 'type IN (%Ls)', $this->types); } if ($this->names) { $where[] = qsprintf( $conn_r, 'name IN (%Ls)', $this->names); } if ($this->titles) { $hashes = array(); foreach ($this->titles as $title) { $slug = DivinerAtomRef::normalizeTitleString($title); $hash = PhabricatorHash::digestForIndex($slug); $hashes[] = $hash; } $where[] = qsprintf( $conn_r, 'titleSlugHash in (%Ls)', $hashes); } if ($this->contexts) { $with_null = false; $contexts = $this->contexts; foreach ($contexts as $key => $value) { if ($value === null) { unset($contexts[$key]); $with_null = true; continue; } } if ($contexts && $with_null) { $where[] = qsprintf( $conn_r, 'context IN (%Ls) OR context IS NULL', $contexts); } else if ($contexts) { $where[] = qsprintf( $conn_r, 'context IN (%Ls)', $contexts); } else if ($with_null) { $where[] = qsprintf( $conn_r, 'context IS NULL'); } } if ($this->indexes) { $where[] = qsprintf( $conn_r, 'atomIndex IN (%Ld)', $this->indexes); } if (!$this->includeUndocumentable) { $where[] = qsprintf( $conn_r, 'isDocumentable = 1'); } if (!$this->includeGhosts) { $where[] = qsprintf( $conn_r, 'graphHash IS NOT NULL'); } if ($this->nodeHashes) { $where[] = qsprintf( $conn_r, 'nodeHash IN (%Ls)', $this->nodeHashes); } if ($this->nameContains) { // NOTE: This CONVERT() call makes queries case-insensitive, since the // column has binary collation. Eventually, this should move into // fulltext. $where[] = qsprintf( $conn_r, 'CONVERT(name USING utf8) LIKE %~', $this->nameContains); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } /** * Walk a list of atoms and collect all the node hashes of the atoms' * children. When recursing, also walk up the tree and collect children of * atoms they extend. * * @param list List of symbols to collect child hashes of. * @param bool True to collect children of extended atoms, * as well. * @return map Hashes of atoms' children. */ private function getAllChildHashes(array $symbols, $recurse_up) { assert_instances_of($symbols, 'DivinerLiveSymbol'); $hashes = array(); foreach ($symbols as $symbol) { foreach ($symbol->getAtom()->getChildHashes() as $hash) { $hashes[$hash] = $hash; } if ($recurse_up) { $hashes += $this->getAllChildHashes($symbol->getExtends(), true); } } return $hashes; } /** * Attach child atoms to existing atoms. In recursive mode, also attach child * atoms to atoms that these atoms extend. * * @param list List of symbols to attach children to. * @param map Map of symbols, keyed by node hash. * @param bool True to attach children to extended atoms, as well. * @return void */ private function attachAllChildren( array $symbols, array $children, $recurse_up) { assert_instances_of($symbols, 'DivinerLiveSymbol'); assert_instances_of($children, 'DivinerLiveSymbol'); foreach ($symbols as $symbol) { $symbol_children = array(); foreach ($symbol->getAtom()->getChildHashes() as $hash) { if (isset($children[$hash])) { $symbol_children[] = $children[$hash]; } } $symbol->attachChildren($symbol_children); if ($recurse_up) { $this->attachAllChildren($symbol->getExtends(), $children, true); } } } public function getQueryApplicationClass() { return 'PhabricatorDivinerApplication'; } } diff --git a/src/applications/diviner/query/DivinerBookQuery.php b/src/applications/diviner/query/DivinerBookQuery.php index 31e6f336a9..90fed52a23 100644 --- a/src/applications/diviner/query/DivinerBookQuery.php +++ b/src/applications/diviner/query/DivinerBookQuery.php @@ -1,72 +1,72 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withNames(array $names) { $this->names = $names; return $this; } protected function loadPage() { $table = new DivinerLiveBook(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->names) { $where[] = qsprintf( $conn_r, 'name IN (%Ls)', $this->names); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorDivinerApplication'; } } diff --git a/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php b/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php index 75ace905da..770678a687 100644 --- a/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php +++ b/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php @@ -1,59 +1,59 @@ phids = $phids; return $this; } public function withObjectKeys(array $keys) { $this->objectKeys = $keys; return $this; } protected function loadPage() { $table = new DoorkeeperExternalObject(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->objectKeys) { $where[] = qsprintf( $conn_r, 'objectKey IN (%Ls)', $this->objectKeys); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorDoorkeeperApplication'; } } diff --git a/src/applications/drydock/query/DrydockBlueprintQuery.php b/src/applications/drydock/query/DrydockBlueprintQuery.php index 4afdb582c2..b698855bcf 100644 --- a/src/applications/drydock/query/DrydockBlueprintQuery.php +++ b/src/applications/drydock/query/DrydockBlueprintQuery.php @@ -1,65 +1,65 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } protected function loadPage() { $table = new DrydockBlueprint(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT blueprint.* FROM %T blueprint %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $blueprints = $table->loadAllFromArray($data); $implementations = DrydockBlueprintImplementation::getAllBlueprintImplementations(); foreach ($blueprints as $blueprint) { if (array_key_exists($blueprint->getClassName(), $implementations)) { $blueprint->attachImplementation( $implementations[$blueprint->getClassName()]); } } return $blueprints; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } return $this->formatWhereClause($where); } } diff --git a/src/applications/drydock/query/DrydockLeaseQuery.php b/src/applications/drydock/query/DrydockLeaseQuery.php index 028351bdae..d5d62f950e 100644 --- a/src/applications/drydock/query/DrydockLeaseQuery.php +++ b/src/applications/drydock/query/DrydockLeaseQuery.php @@ -1,108 +1,108 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withResourceIDs(array $ids) { $this->resourceIDs = $ids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } protected function loadPage() { $table = new DrydockLease(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT lease.* FROM %T lease %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $leases) { $resource_ids = array_filter(mpull($leases, 'getResourceID')); if ($resource_ids) { $resources = id(new DrydockResourceQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withIDs($resource_ids) ->execute(); } else { $resources = array(); } foreach ($leases as $key => $lease) { $resource = null; if ($lease->getResourceID()) { $resource = idx($resources, $lease->getResourceID()); if (!$resource) { unset($leases[$key]); continue; } } $lease->attachResource($resource); } return $leases; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->resourceIDs) { $where[] = qsprintf( $conn_r, 'resourceID IN (%Ld)', $this->resourceIDs); } if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->statuses) { $where[] = qsprintf( $conn_r, 'status IN (%Ld)', $this->statuses); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } } diff --git a/src/applications/drydock/query/DrydockLogQuery.php b/src/applications/drydock/query/DrydockLogQuery.php index 83413d79c0..1e21864f10 100644 --- a/src/applications/drydock/query/DrydockLogQuery.php +++ b/src/applications/drydock/query/DrydockLogQuery.php @@ -1,113 +1,113 @@ resourceIDs = $ids; return $this; } public function withLeaseIDs(array $ids) { $this->leaseIDs = $ids; return $this; } protected function loadPage() { $table = new DrydockLog(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT log.* FROM %T log %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $logs) { $resource_ids = array_filter(mpull($logs, 'getResourceID')); if ($resource_ids) { $resources = id(new DrydockResourceQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withIDs($resource_ids) ->execute(); } else { $resources = array(); } foreach ($logs as $key => $log) { $resource = null; if ($log->getResourceID()) { $resource = idx($resources, $log->getResourceID()); if (!$resource) { unset($logs[$key]); continue; } } $log->attachResource($resource); } $lease_ids = array_filter(mpull($logs, 'getLeaseID')); if ($lease_ids) { $leases = id(new DrydockLeaseQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withIDs($lease_ids) ->execute(); } else { $leases = array(); } foreach ($logs as $key => $log) { $lease = null; if ($log->getLeaseID()) { $lease = idx($leases, $log->getLeaseID()); if (!$lease) { unset($logs[$key]); continue; } } $log->attachLease($lease); } // These logs are meaningless and their policies aren't computable. They // shouldn't exist, but throw them away if they do. foreach ($logs as $key => $log) { if (!$log->getResource() && !$log->getLease()) { unset($logs[$key]); } } return $logs; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->resourceIDs) { $where[] = qsprintf( $conn_r, 'resourceID IN (%Ld)', $this->resourceIDs); } if ($this->leaseIDs) { $where[] = qsprintf( $conn_r, 'leaseID IN (%Ld)', $this->leaseIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } } diff --git a/src/applications/drydock/query/DrydockResourceQuery.php b/src/applications/drydock/query/DrydockResourceQuery.php index 468af91db4..af84fefa4a 100644 --- a/src/applications/drydock/query/DrydockResourceQuery.php +++ b/src/applications/drydock/query/DrydockResourceQuery.php @@ -1,96 +1,96 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withTypes(array $types) { $this->types = $types; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function withBlueprintPHIDs(array $blueprint_phids) { $this->blueprintPHIDs = $blueprint_phids; return $this; } protected function loadPage() { $table = new DrydockResource(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT resource.* FROM %T resource %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $resources = $table->loadAllFromArray($data); return $resources; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->types) { $where[] = qsprintf( $conn_r, 'type IN (%Ls)', $this->types); } if ($this->statuses) { $where[] = qsprintf( $conn_r, 'status IN (%Ls)', $this->statuses); } if ($this->blueprintPHIDs) { $where[] = qsprintf( $conn_r, 'blueprintPHID IN (%Ls)', $this->blueprintPHIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } } diff --git a/src/applications/feed/query/PhabricatorFeedQuery.php b/src/applications/feed/query/PhabricatorFeedQuery.php index 5f3efe5aaf..424658b342 100644 --- a/src/applications/feed/query/PhabricatorFeedQuery.php +++ b/src/applications/feed/query/PhabricatorFeedQuery.php @@ -1,126 +1,126 @@ filterPHIDs = $phids; return $this; } public function withChronologicalKeys(array $keys) { $this->chronologicalKeys = $keys; return $this; } protected function loadPage() { $story_table = new PhabricatorFeedStoryData(); $conn = $story_table->establishConnection('r'); $data = queryfx_all( $conn, 'SELECT story.* FROM %T story %Q %Q %Q %Q %Q', $story_table->getTableName(), $this->buildJoinClause($conn), $this->buildWhereClause($conn), $this->buildGroupClause($conn), $this->buildOrderClause($conn), $this->buildLimitClause($conn)); return $data; } protected function willFilterPage(array $data) { return PhabricatorFeedStory::loadAllFromRows($data, $this->getViewer()); } private function buildJoinClause(AphrontDatabaseConnection $conn_r) { // NOTE: We perform this join unconditionally (even if we have no filter // PHIDs) to omit rows which have no story references. These story data // rows are notifications or realtime alerts. $ref_table = new PhabricatorFeedStoryReference(); return qsprintf( $conn_r, 'JOIN %T ref ON ref.chronologicalKey = story.chronologicalKey', $ref_table->getTableName()); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->filterPHIDs) { $where[] = qsprintf( $conn_r, 'ref.objectPHID IN (%Ls)', $this->filterPHIDs); } if ($this->chronologicalKeys) { // NOTE: We want to use integers in the query so we can take advantage // of keys, but can't use %d on 32-bit systems. Make sure all the keys // are integers and then format them raw. $keys = $this->chronologicalKeys; foreach ($keys as $key) { if (!ctype_digit($key)) { throw new Exception("Key '{$key}' is not a valid chronological key!"); } } $where[] = qsprintf( $conn_r, 'ref.chronologicalKey IN (%Q)', implode(', ', $keys)); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } private function buildGroupClause(AphrontDatabaseConnection $conn_r) { if ($this->filterPHIDs) { return qsprintf($conn_r, 'GROUP BY ref.chronologicalKey'); } else { return qsprintf($conn_r, 'GROUP BY story.chronologicalKey'); } } protected function getDefaultOrderVector() { return array('key'); } public function getOrderableColumns() { $table = ($this->filterPHIDs ? 'ref' : 'story'); return array( 'key' => array( 'table' => $table, 'column' => 'chronologicalKey', 'type' => 'int', 'unique' => true, ), ); } protected function getPagingValueMap($cursor, array $keys) { return array( 'key' => $cursor, ); } protected function getResultCursor($item) { if ($item instanceof PhabricatorFeedStory) { return $item->getChronologicalKey(); } return $item['chronologicalKey']; } public function getQueryApplicationClass() { return 'PhabricatorFeedApplication'; } } diff --git a/src/applications/files/query/PhabricatorFileChunkQuery.php b/src/applications/files/query/PhabricatorFileChunkQuery.php index 7c2a961fd0..b6fda13103 100644 --- a/src/applications/files/query/PhabricatorFileChunkQuery.php +++ b/src/applications/files/query/PhabricatorFileChunkQuery.php @@ -1,134 +1,134 @@ chunkHandles = $handles; return $this; } public function withByteRange($start, $end) { $this->rangeStart = $start; $this->rangeEnd = $end; return $this; } public function withIsComplete($complete) { $this->isComplete = $complete; return $this; } public function needDataFiles($need) { $this->needDataFiles = $need; return $this; } protected function loadPage() { $table = new PhabricatorFileChunk(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $chunks) { if ($this->needDataFiles) { $file_phids = mpull($chunks, 'getDataFilePHID'); $file_phids = array_filter($file_phids); if ($file_phids) { $files = id(new PhabricatorFileQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($file_phids) ->execute(); $files = mpull($files, null, 'getPHID'); } else { $files = array(); } foreach ($chunks as $key => $chunk) { $data_phid = $chunk->getDataFilePHID(); if (!$data_phid) { $chunk->attachDataFile(null); continue; } $file = idx($files, $data_phid); if (!$file) { unset($chunks[$key]); $this->didRejectResult($chunk); continue; } $chunk->attachDataFile($file); } if (!$chunks) { return $chunks; } } return $chunks; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->chunkHandles !== null) { $where[] = qsprintf( $conn_r, 'chunkHandle IN (%Ls)', $this->chunkHandles); } if ($this->rangeStart !== null) { $where[] = qsprintf( $conn_r, 'byteEnd > %d', $this->rangeStart); } if ($this->rangeEnd !== null) { $where[] = qsprintf( $conn_r, 'byteStart < %d', $this->rangeEnd); } if ($this->isComplete !== null) { if ($this->isComplete) { $where[] = qsprintf( $conn_r, 'dataFilePHID IS NOT NULL'); } else { $where[] = qsprintf( $conn_r, 'dataFilePHID IS NULL'); } } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorFilesApplication'; } } diff --git a/src/applications/files/query/PhabricatorFileQuery.php b/src/applications/files/query/PhabricatorFileQuery.php index ad0bbbac1b..a15be0c0bb 100644 --- a/src/applications/files/query/PhabricatorFileQuery.php +++ b/src/applications/files/query/PhabricatorFileQuery.php @@ -1,342 +1,342 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs(array $phids) { $this->authorPHIDs = $phids; return $this; } public function withDateCreatedBefore($date_created_before) { $this->dateCreatedBefore = $date_created_before; return $this; } public function withDateCreatedAfter($date_created_after) { $this->dateCreatedAfter = $date_created_after; return $this; } public function withContentHashes(array $content_hashes) { $this->contentHashes = $content_hashes; return $this; } /** * Select files which are transformations of some other file. For example, * you can use this query to find previously generated thumbnails of an image * file. * * As a parameter, provide a list of transformation specifications. Each * specification is a dictionary with the keys `originalPHID` and `transform`. * The `originalPHID` is the PHID of the original file (the file which was * transformed) and the `transform` is the name of the transform to query * for. If you pass `true` as the `transform`, all transformations of the * file will be selected. * * For example: * * array( * array( * 'originalPHID' => 'PHID-FILE-aaaa', * 'transform' => 'sepia', * ), * array( * 'originalPHID' => 'PHID-FILE-bbbb', * 'transform' => true, * ), * ) * * This selects the `"sepia"` transformation of the file with PHID * `PHID-FILE-aaaa` and all transformations of the file with PHID * `PHID-FILE-bbbb`. * * @param list List of transform specifications, described above. * @return this */ public function withTransforms(array $specs) { foreach ($specs as $spec) { if (!is_array($spec) || empty($spec['originalPHID']) || empty($spec['transform'])) { throw new Exception( "Transform specification must be a dictionary with keys ". "'originalPHID' and 'transform'!"); } } $this->transforms = $specs; return $this; } public function withLengthBetween($min, $max) { $this->minLength = $min; $this->maxLength = $max; return $this; } public function withNames(array $names) { $this->names = $names; return $this; } public function withIsPartial($partial) { $this->isPartial = $partial; return $this; } public function showOnlyExplicitUploads($explicit_uploads) { $this->explicitUploads = $explicit_uploads; return $this; } protected function loadPage() { $table = new PhabricatorFile(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT f.* FROM %T f %Q %Q %Q %Q', $table->getTableName(), $this->buildJoinClause($conn_r), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $files = $table->loadAllFromArray($data); if (!$files) { return $files; } // We need to load attached objects to perform policy checks for files. // First, load the edges. $edge_type = PhabricatorFileHasObjectEdgeType::EDGECONST; $file_phids = mpull($files, 'getPHID'); $edges = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs($file_phids) ->withEdgeTypes(array($edge_type)) ->execute(); $object_phids = array(); foreach ($files as $file) { $phids = array_keys($edges[$file->getPHID()][$edge_type]); $file->attachObjectPHIDs($phids); foreach ($phids as $phid) { $object_phids[$phid] = true; } } // If this file is a transform of another file, load that file too. If you // can see the original file, you can see the thumbnail. // TODO: It might be nice to put this directly on PhabricatorFile and remove // the PhabricatorTransformedFile table, which would be a little simpler. $xforms = id(new PhabricatorTransformedFile())->loadAllWhere( 'transformedPHID IN (%Ls)', $file_phids); $xform_phids = mpull($xforms, 'getOriginalPHID', 'getTransformedPHID'); foreach ($xform_phids as $derived_phid => $original_phid) { $object_phids[$original_phid] = true; } $object_phids = array_keys($object_phids); // Now, load the objects. $objects = array(); if ($object_phids) { // NOTE: We're explicitly turning policy exceptions off, since the rule // here is "you can see the file if you can see ANY associated object". // Without this explicit flag, we'll incorrectly throw unless you can // see ALL associated objects. $objects = id(new PhabricatorObjectQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($object_phids) ->setRaisePolicyExceptions(false) ->execute(); $objects = mpull($objects, null, 'getPHID'); } foreach ($files as $file) { $file_objects = array_select_keys($objects, $file->getObjectPHIDs()); $file->attachObjects($file_objects); } foreach ($files as $key => $file) { $original_phid = idx($xform_phids, $file->getPHID()); if ($original_phid == PhabricatorPHIDConstants::PHID_VOID) { // This is a special case for builtin files, which are handled // oddly. $original = null; } else if ($original_phid) { $original = idx($objects, $original_phid); if (!$original) { // If the viewer can't see the original file, also prevent them from // seeing the transformed file. $this->didRejectResult($file); unset($files[$key]); continue; } } else { $original = null; } $file->attachOriginalFile($original); } return $files; } private function buildJoinClause(AphrontDatabaseConnection $conn_r) { $joins = array(); if ($this->transforms) { $joins[] = qsprintf( $conn_r, 'JOIN %T t ON t.transformedPHID = f.phid', id(new PhabricatorTransformedFile())->getTableName()); } return implode(' ', $joins); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); $where[] = $this->buildPagingClause($conn_r); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'f.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'f.phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn_r, 'f.authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->explicitUploads !== null) { $where[] = qsprintf( $conn_r, 'f.isExplicitUpload = true'); } if ($this->transforms !== null) { $clauses = array(); foreach ($this->transforms as $transform) { if ($transform['transform'] === true) { $clauses[] = qsprintf( $conn_r, '(t.originalPHID = %s)', $transform['originalPHID']); } else { $clauses[] = qsprintf( $conn_r, '(t.originalPHID = %s AND t.transform = %s)', $transform['originalPHID'], $transform['transform']); } } $where[] = qsprintf($conn_r, '(%Q)', implode(') OR (', $clauses)); } if ($this->dateCreatedAfter !== null) { $where[] = qsprintf( $conn_r, 'f.dateCreated >= %d', $this->dateCreatedAfter); } if ($this->dateCreatedBefore !== null) { $where[] = qsprintf( $conn_r, 'f.dateCreated <= %d', $this->dateCreatedBefore); } if ($this->contentHashes !== null) { $where[] = qsprintf( $conn_r, 'f.contentHash IN (%Ls)', $this->contentHashes); } if ($this->minLength !== null) { $where[] = qsprintf( $conn_r, 'byteSize >= %d', $this->minLength); } if ($this->maxLength !== null) { $where[] = qsprintf( $conn_r, 'byteSize <= %d', $this->maxLength); } if ($this->names !== null) { $where[] = qsprintf( $conn_r, 'name in (%Ls)', $this->names); } if ($this->isPartial !== null) { $where[] = qsprintf( $conn_r, 'isPartial = %d', (int)$this->isPartial); } return $this->formatWhereClause($where); } protected function getPrimaryTableAlias() { return 'f'; } public function getQueryApplicationClass() { return 'PhabricatorFilesApplication'; } } diff --git a/src/applications/flag/query/PhabricatorFlagQuery.php b/src/applications/flag/query/PhabricatorFlagQuery.php index 910ed7dfa9..0b1902f6f1 100644 --- a/src/applications/flag/query/PhabricatorFlagQuery.php +++ b/src/applications/flag/query/PhabricatorFlagQuery.php @@ -1,165 +1,165 @@ ownerPHIDs = $owner_phids; return $this; } public function withTypes(array $types) { $this->types = $types; return $this; } public function withObjectPHIDs(array $object_phids) { $this->objectPHIDs = $object_phids; return $this; } public function withColors(array $colors) { $this->colors = $colors; return $this; } /** * NOTE: this is done in PHP and not in MySQL, which means its inappropriate * for large datasets. Pragmatically, this is fine for user flags which are * typically well under 100 flags per user. */ public function setGroupBy($group) { $this->groupBy = $group; return $this; } public function needHandles($need) { $this->needHandles = $need; return $this; } public function needObjects($need) { $this->needObjects = $need; return $this; } public static function loadUserFlag(PhabricatorUser $user, $object_phid) { // Specifying the type in the query allows us to use a key. return id(new PhabricatorFlagQuery()) ->setViewer($user) ->withOwnerPHIDs(array($user->getPHID())) ->withTypes(array(phid_get_type($object_phid))) ->withObjectPHIDs(array($object_phid)) ->executeOne(); } protected function loadPage() { $table = new PhabricatorFlag(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T flag %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $flags) { if ($this->needObjects) { $objects = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs(mpull($flags, 'getObjectPHID')) ->execute(); $objects = mpull($objects, null, 'getPHID'); foreach ($flags as $key => $flag) { $object = idx($objects, $flag->getObjectPHID()); if ($object) { $flags[$key]->attachObject($object); } else { unset($flags[$key]); } } } if ($this->needHandles) { $handles = id(new PhabricatorHandleQuery()) ->setViewer($this->getViewer()) ->withPHIDs(mpull($flags, 'getObjectPHID')) ->execute(); foreach ($flags as $flag) { $flag->attachHandle($handles[$flag->getObjectPHID()]); } } switch ($this->groupBy) { case self::GROUP_COLOR: $flags = msort($flags, 'getColor'); break; case self::GROUP_NONE: break; default: throw new Exception("Unknown groupBy parameter: $this->groupBy"); break; } return $flags; } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ownerPHIDs) { $where[] = qsprintf( $conn_r, 'flag.ownerPHID IN (%Ls)', $this->ownerPHIDs); } if ($this->types) { $where[] = qsprintf( $conn_r, 'flag.type IN (%Ls)', $this->types); } if ($this->objectPHIDs) { $where[] = qsprintf( $conn_r, 'flag.objectPHID IN (%Ls)', $this->objectPHIDs); } if ($this->colors) { $where[] = qsprintf( $conn_r, 'flag.color IN (%Ld)', $this->colors); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorFlagsApplication'; } } diff --git a/src/applications/fund/query/FundBackerQuery.php b/src/applications/fund/query/FundBackerQuery.php index 505de2bdf3..5f7406f5ad 100644 --- a/src/applications/fund/query/FundBackerQuery.php +++ b/src/applications/fund/query/FundBackerQuery.php @@ -1,118 +1,118 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function withInitiativePHIDs(array $phids) { $this->initiativePHIDs = $phids; return $this; } public function withBackerPHIDs(array $phids) { $this->backerPHIDs = $phids; return $this; } protected function loadPage() { $table = new FundBacker(); $conn_r = $table->establishConnection('r'); $rows = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($rows); } protected function willFilterPage(array $backers) { $initiative_phids = mpull($backers, 'getInitiativePHID'); $initiatives = id(new PhabricatorObjectQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($initiative_phids) ->execute(); $initiatives = mpull($initiatives, null, 'getPHID'); foreach ($backers as $backer) { $initiative_phid = $backer->getInitiativePHID(); $initiative = idx($initiatives, $initiative_phid); $backer->attachInitiative($initiative); } return $backers; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); $where[] = $this->buildPagingClause($conn_r); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->initiativePHIDs !== null) { $where[] = qsprintf( $conn_r, 'initiativePHID IN (%Ls)', $this->initiativePHIDs); } if ($this->backerPHIDs !== null) { $where[] = qsprintf( $conn_r, 'backerPHID IN (%Ls)', $this->backerPHIDs); } if ($this->statuses !== null) { $where[] = qsprintf( $conn_r, 'status IN (%Ls)', $this->statuses); } return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorFundApplication'; } } diff --git a/src/applications/fund/query/FundInitiativeQuery.php b/src/applications/fund/query/FundInitiativeQuery.php index 8fd910c046..66f87a883c 100644 --- a/src/applications/fund/query/FundInitiativeQuery.php +++ b/src/applications/fund/query/FundInitiativeQuery.php @@ -1,116 +1,116 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withOwnerPHIDs(array $phids) { $this->ownerPHIDs = $phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function needProjectPHIDs($need) { $this->needProjectPHIDs = $need; return $this; } protected function loadPage() { $table = new FundInitiative(); $conn_r = $table->establishConnection('r'); $rows = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($rows); } protected function didFilterPage(array $initiatives) { if ($this->needProjectPHIDs) { $edge_query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(mpull($initiatives, 'getPHID')) ->withEdgeTypes( array( PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, )); $edge_query->execute(); foreach ($initiatives as $initiative) { $phids = $edge_query->getDestinationPHIDs( array( $initiative->getPHID(), )); $initiative->attachProjectPHIDs($phids); } } return $initiatives; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); $where[] = $this->buildPagingClause($conn_r); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->ownerPHIDs !== null) { $where[] = qsprintf( $conn_r, 'ownerPHID IN (%Ls)', $this->ownerPHIDs); } if ($this->statuses !== null) { $where[] = qsprintf( $conn_r, 'status IN (%Ls)', $this->statuses); } return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorFundApplication'; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php b/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php index 077e77a4bd..553780aa76 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php @@ -1,124 +1,124 @@ ids = $ids; return $this; } public function withBuildTargetPHIDs(array $build_target_phids) { $this->buildTargetPHIDs = $build_target_phids; return $this; } public function withArtifactTypes(array $artifact_types) { $this->artifactTypes = $artifact_types; return $this; } public function withArtifactKeys( $build_phid, $build_gen, array $artifact_keys) { $this->keyBuildPHID = $build_phid; $this->keyBuildGeneration = $build_gen; $this->artifactKeys = $artifact_keys; return $this; } protected function loadPage() { $table = new HarbormasterBuildArtifact(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $page) { $build_targets = array(); $build_target_phids = array_filter(mpull($page, 'getBuildTargetPHID')); if ($build_target_phids) { $build_targets = id(new HarbormasterBuildTargetQuery()) ->setViewer($this->getViewer()) ->withPHIDs($build_target_phids) ->setParentQuery($this) ->execute(); $build_targets = mpull($build_targets, null, 'getPHID'); } foreach ($page as $key => $build_log) { $build_target_phid = $build_log->getBuildTargetPHID(); if (empty($build_targets[$build_target_phid])) { unset($page[$key]); continue; } $build_log->attachBuildTarget($build_targets[$build_target_phid]); } return $page; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->buildTargetPHIDs) { $where[] = qsprintf( $conn_r, 'buildTargetPHID IN (%Ls)', $this->buildTargetPHIDs); } if ($this->artifactTypes) { $where[] = qsprintf( $conn_r, 'artifactType in (%Ls)', $this->artifactTypes); } if ($this->artifactKeys) { $indexes = array(); foreach ($this->artifactKeys as $key) { $indexes[] = PhabricatorHash::digestForIndex( $this->keyBuildPHID.$this->keyBuildGeneration.$key); } $where[] = qsprintf( $conn_r, 'artifactIndex IN (%Ls)', $indexes); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorHarbormasterApplication'; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildItemQuery.php b/src/applications/harbormaster/query/HarbormasterBuildItemQuery.php index 2c82588940..1c54cf1fa7 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildItemQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildItemQuery.php @@ -1,60 +1,60 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } protected function loadPage() { $table = new HarbormasterBuildItem(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid in (%Ls)', $this->phids); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorHarbormasterApplication'; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildLogQuery.php b/src/applications/harbormaster/query/HarbormasterBuildLogQuery.php index 461697a1e4..f0f2021fd8 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildLogQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildLogQuery.php @@ -1,98 +1,98 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withBuildTargetPHIDs(array $build_target_phids) { $this->buildTargetPHIDs = $build_target_phids; return $this; } protected function loadPage() { $table = new HarbormasterBuildLog(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $page) { $build_targets = array(); $build_target_phids = array_filter(mpull($page, 'getBuildTargetPHID')); if ($build_target_phids) { $build_targets = id(new HarbormasterBuildTargetQuery()) ->setViewer($this->getViewer()) ->withPHIDs($build_target_phids) ->setParentQuery($this) ->execute(); $build_targets = mpull($build_targets, null, 'getPHID'); } foreach ($page as $key => $build_log) { $build_target_phid = $build_log->getBuildTargetPHID(); if (empty($build_targets[$build_target_phid])) { unset($page[$key]); continue; } $build_log->attachBuildTarget($build_targets[$build_target_phid]); } return $page; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->buildTargetPHIDs) { $where[] = qsprintf( $conn_r, 'buildTargetPHID IN (%Ls)', $this->buildTargetPHIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorHarbormasterApplication'; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildMessageQuery.php b/src/applications/harbormaster/query/HarbormasterBuildMessageQuery.php index 816bafc1ad..749fdd76d4 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildMessageQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildMessageQuery.php @@ -1,98 +1,98 @@ ids = $ids; return $this; } public function withBuildTargetPHIDs(array $phids) { $this->buildTargetPHIDs = $phids; return $this; } public function withConsumed($consumed) { $this->consumed = $consumed; return $this; } protected function loadPage() { $table = new HarbormasterBuildMessage(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $page) { $build_target_phids = array_filter(mpull($page, 'getBuildTargetPHID')); if ($build_target_phids) { $build_targets = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($build_target_phids) ->setParentQuery($this) ->execute(); $build_targets = mpull($build_targets, null, 'getPHID'); } else { $build_targets = array(); } foreach ($page as $key => $message) { $build_target_phid = $message->getBuildTargetPHID(); if (empty($build_targets[$build_target_phid])) { unset($page[$key]); continue; } $message->attachBuildTarget($build_targets[$build_target_phid]); } return $page; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->buildTargetPHIDs) { $where[] = qsprintf( $conn_r, 'buildTargetPHID IN (%Ls)', $this->buildTargetPHIDs); } if ($this->consumed !== null) { $where[] = qsprintf( $conn_r, 'isConsumed = %d', (int)$this->consumed); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorHarbormasterApplication'; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildPlanQuery.php b/src/applications/harbormaster/query/HarbormasterBuildPlanQuery.php index 8ea76ba36f..4123941665 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildPlanQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildPlanQuery.php @@ -1,113 +1,113 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function withDatasourceQuery($query) { $this->datasourceQuery = $query; return $this; } protected function loadPage() { $table = new HarbormasterBuildPlan(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->statuses) { $where[] = qsprintf( $conn_r, 'planStatus IN (%Ls)', $this->statuses); } if (strlen($this->datasourceQuery)) { $where[] = qsprintf( $conn_r, 'name LIKE %>', $this->datasourceQuery); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorHarbormasterApplication'; } public function getOrderableColumns() { return parent::getOrderableColumns() + array( 'name' => array( 'column' => 'name', 'type' => 'string', 'reverse' => true, ), ); } protected function getPagingValueMap($cursor, array $keys) { $plan = $this->loadCursorObject($cursor); return array( 'id' => $plan->getID(), 'name' => $plan->getName(), ); } public function getBuiltinOrders() { return array( 'name' => array( 'vector' => array('name', 'id'), 'name' => pht('Name'), ), ) + parent::getBuiltinOrders(); } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildQuery.php b/src/applications/harbormaster/query/HarbormasterBuildQuery.php index 0daf5ac906..d0b1e7420c 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildQuery.php @@ -1,186 +1,186 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withBuildStatuses(array $build_statuses) { $this->buildStatuses = $build_statuses; return $this; } public function withBuildablePHIDs(array $buildable_phids) { $this->buildablePHIDs = $buildable_phids; return $this; } public function withBuildPlanPHIDs(array $build_plan_phids) { $this->buildPlanPHIDs = $build_plan_phids; return $this; } public function needBuildTargets($need_targets) { $this->needBuildTargets = $need_targets; return $this; } protected function loadPage() { $table = new HarbormasterBuild(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $page) { $buildables = array(); $buildable_phids = array_filter(mpull($page, 'getBuildablePHID')); if ($buildable_phids) { $buildables = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($buildable_phids) ->setParentQuery($this) ->execute(); $buildables = mpull($buildables, null, 'getPHID'); } foreach ($page as $key => $build) { $buildable_phid = $build->getBuildablePHID(); if (empty($buildables[$buildable_phid])) { unset($page[$key]); continue; } $build->attachBuildable($buildables[$buildable_phid]); } return $page; } protected function didFilterPage(array $page) { $plans = array(); $plan_phids = array_filter(mpull($page, 'getBuildPlanPHID')); if ($plan_phids) { $plans = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($plan_phids) ->setParentQuery($this) ->execute(); $plans = mpull($plans, null, 'getPHID'); } foreach ($page as $key => $build) { $plan_phid = $build->getBuildPlanPHID(); $build->attachBuildPlan(idx($plans, $plan_phid)); } $build_phids = mpull($page, 'getPHID'); $commands = id(new HarbormasterBuildCommand())->loadAllWhere( 'targetPHID IN (%Ls) ORDER BY id ASC', $build_phids); $commands = mgroup($commands, 'getTargetPHID'); foreach ($page as $build) { $unprocessed_commands = idx($commands, $build->getPHID(), array()); $build->attachUnprocessedCommands($unprocessed_commands); } if ($this->needBuildTargets) { $targets = id(new HarbormasterBuildTargetQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withBuildPHIDs($build_phids) ->execute(); // TODO: Some day, when targets have dependencies, we should toposort // these. For now, just put them into chronological order. $targets = array_reverse($targets); $targets = mgroup($targets, 'getBuildPHID'); foreach ($page as $build) { $build_targets = idx($targets, $build->getPHID(), array()); foreach ($build_targets as $phid => $target) { if ($target->getBuildGeneration() !== $build->getBuildGeneration()) { unset($build_targets[$phid]); } } $build->attachBuildTargets($build_targets); } } return $page; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'phid in (%Ls)', $this->phids); } if ($this->buildStatuses !== null) { $where[] = qsprintf( $conn_r, 'buildStatus in (%Ls)', $this->buildStatuses); } if ($this->buildablePHIDs !== null) { $where[] = qsprintf( $conn_r, 'buildablePHID IN (%Ls)', $this->buildablePHIDs); } if ($this->buildPlanPHIDs !== null) { $where[] = qsprintf( $conn_r, 'buildPlanPHID IN (%Ls)', $this->buildPlanPHIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorHarbormasterApplication'; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php b/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php index bea3ec10f0..c14185116c 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php @@ -1,98 +1,98 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withBuildPlanPHIDs(array $phids) { $this->buildPlanPHIDs = $phids; return $this; } protected function loadPage() { $table = new HarbormasterBuildStep(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid in (%Ls)', $this->phids); } if ($this->buildPlanPHIDs) { $where[] = qsprintf( $conn_r, 'buildPlanPHID in (%Ls)', $this->buildPlanPHIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } protected function willFilterPage(array $page) { $plans = array(); $buildplan_phids = array_filter(mpull($page, 'getBuildPlanPHID')); if ($buildplan_phids) { $plans = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($buildplan_phids) ->setParentQuery($this) ->execute(); $plans = mpull($plans, null, 'getPHID'); } foreach ($page as $key => $build) { $buildable_phid = $build->getBuildPlanPHID(); if (empty($plans[$buildable_phid])) { unset($page[$key]); continue; } $build->attachBuildPlan($plans[$buildable_phid]); } return $page; } public function getQueryApplicationClass() { return 'PhabricatorHarbormasterApplication'; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php b/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php index 213a6a4a20..c61c0bd8b0 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php @@ -1,142 +1,142 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withBuildPHIDs(array $build_phids) { $this->buildPHIDs = $build_phids; return $this; } public function withBuildGenerations(array $build_generations) { $this->buildGenerations = $build_generations; return $this; } public function needBuildSteps($need_build_steps) { $this->needBuildSteps = $need_build_steps; return $this; } protected function loadPage() { $table = new HarbormasterBuildTarget(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid in (%Ls)', $this->phids); } if ($this->buildPHIDs) { $where[] = qsprintf( $conn_r, 'buildPHID in (%Ls)', $this->buildPHIDs); } if ($this->buildGenerations) { $where[] = qsprintf( $conn_r, 'buildGeneration in (%Ld)', $this->buildGenerations); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } protected function didFilterPage(array $page) { if ($this->needBuildSteps) { $step_phids = array(); foreach ($page as $target) { $step_phids[] = $target->getBuildStepPHID(); } $steps = id(new HarbormasterBuildStepQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($step_phids) ->execute(); $steps = mpull($steps, null, 'getPHID'); foreach ($page as $target) { $target->attachBuildStep( idx($steps, $target->getBuildStepPHID())); } } return $page; } protected function willFilterPage(array $page) { $builds = array(); $build_phids = array_filter(mpull($page, 'getBuildPHID')); if ($build_phids) { $builds = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($build_phids) ->setParentQuery($this) ->execute(); $builds = mpull($builds, null, 'getPHID'); } foreach ($page as $key => $build_target) { $build_phid = $build_target->getBuildPHID(); if (empty($builds[$build_phid])) { unset($page[$key]); continue; } $build_target->attachBuild($builds[$build_phid]); } return $page; } public function getQueryApplicationClass() { return 'PhabricatorHarbormasterApplication'; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildableQuery.php b/src/applications/harbormaster/query/HarbormasterBuildableQuery.php index 5b5972fa2f..1b1956a0c7 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildableQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildableQuery.php @@ -1,222 +1,222 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withBuildablePHIDs(array $buildable_phids) { $this->buildablePHIDs = $buildable_phids; return $this; } public function withContainerPHIDs(array $container_phids) { $this->containerPHIDs = $container_phids; return $this; } public function withManualBuildables($manual) { $this->manualBuildables = $manual; return $this; } public function needContainerObjects($need) { $this->needContainerObjects = $need; return $this; } public function needContainerHandles($need) { $this->needContainerHandles = $need; return $this; } public function needBuildableHandles($need) { $this->needBuildableHandles = $need; return $this; } public function needBuilds($need) { $this->needBuilds = $need; return $this; } protected function loadPage() { $table = new HarbormasterBuildable(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $page) { $buildables = array(); $buildable_phids = array_filter(mpull($page, 'getBuildablePHID')); if ($buildable_phids) { $buildables = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($buildable_phids) ->setParentQuery($this) ->execute(); $buildables = mpull($buildables, null, 'getPHID'); } foreach ($page as $key => $buildable) { $buildable_phid = $buildable->getBuildablePHID(); if (empty($buildables[$buildable_phid])) { unset($page[$key]); continue; } $buildable->attachBuildableObject($buildables[$buildable_phid]); } return $page; } protected function didFilterPage(array $page) { if ($this->needContainerObjects || $this->needContainerHandles) { $container_phids = array_filter(mpull($page, 'getContainerPHID')); if ($this->needContainerObjects) { $containers = array(); if ($container_phids) { $containers = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($container_phids) ->setParentQuery($this) ->execute(); $containers = mpull($containers, null, 'getPHID'); } foreach ($page as $key => $buildable) { $container_phid = $buildable->getContainerPHID(); $buildable->attachContainerObject(idx($containers, $container_phid)); } } if ($this->needContainerHandles) { $handles = array(); if ($container_phids) { $handles = id(new PhabricatorHandleQuery()) ->setViewer($this->getViewer()) ->withPHIDs($container_phids) ->setParentQuery($this) ->execute(); } foreach ($page as $key => $buildable) { $container_phid = $buildable->getContainerPHID(); $buildable->attachContainerHandle(idx($handles, $container_phid)); } } } if ($this->needBuildableHandles) { $handles = array(); $handle_phids = array_filter(mpull($page, 'getBuildablePHID')); if ($handle_phids) { $handles = id(new PhabricatorHandleQuery()) ->setViewer($this->getViewer()) ->withPHIDs($handle_phids) ->setParentQuery($this) ->execute(); } foreach ($page as $key => $buildable) { $handle_phid = $buildable->getBuildablePHID(); $buildable->attachBuildableHandle(idx($handles, $handle_phid)); } } if ($this->needBuilds) { $builds = id(new HarbormasterBuildQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withBuildablePHIDs(mpull($page, 'getPHID')) ->execute(); $builds = mgroup($builds, 'getBuildablePHID'); foreach ($page as $key => $buildable) { $buildable->attachBuilds(idx($builds, $buildable->getPHID(), array())); } } return $page; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->buildablePHIDs !== null) { $where[] = qsprintf( $conn_r, 'buildablePHID IN (%Ls)', $this->buildablePHIDs); } if ($this->containerPHIDs !== null) { $where[] = qsprintf( $conn_r, 'containerPHID in (%Ls)', $this->containerPHIDs); } if ($this->manualBuildables !== null) { $where[] = qsprintf( $conn_r, 'isManualBuildable = %d', (int)$this->manualBuildables); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorHarbormasterApplication'; } } diff --git a/src/applications/herald/query/HeraldRuleQuery.php b/src/applications/herald/query/HeraldRuleQuery.php index 6e88dc40a0..5967c6ffc9 100644 --- a/src/applications/herald/query/HeraldRuleQuery.php +++ b/src/applications/herald/query/HeraldRuleQuery.php @@ -1,276 +1,276 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs(array $author_phids) { $this->authorPHIDs = $author_phids; return $this; } public function withRuleTypes(array $types) { $this->ruleTypes = $types; return $this; } public function withContentTypes(array $types) { $this->contentTypes = $types; return $this; } public function withExecutableRules($executable) { $this->executable = $executable; return $this; } public function withDisabled($disabled) { $this->disabled = $disabled; return $this; } public function withTriggerObjectPHIDs(array $phids) { $this->triggerObjectPHIDs = $phids; return $this; } public function needConditionsAndActions($need) { $this->needConditionsAndActions = $need; return $this; } public function needAppliedToPHIDs(array $phids) { $this->needAppliedToPHIDs = $phids; return $this; } public function needValidateAuthors($need) { $this->needValidateAuthors = $need; return $this; } protected function loadPage() { $table = new HeraldRule(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT rule.* FROM %T rule %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $rules) { $rule_ids = mpull($rules, 'getID'); // Filter out any rules that have invalid adapters, or have adapters the // viewer isn't permitted to see or use (for example, Differential rules // if the user can't use Differential or Differential is not installed). $types = HeraldAdapter::getEnabledAdapterMap($this->getViewer()); foreach ($rules as $key => $rule) { if (empty($types[$rule->getContentType()])) { $this->didRejectResult($rule); unset($rules[$key]); } } if ($this->needValidateAuthors) { $this->validateRuleAuthors($rules); } if ($this->needConditionsAndActions) { $conditions = id(new HeraldCondition())->loadAllWhere( 'ruleID IN (%Ld)', $rule_ids); $conditions = mgroup($conditions, 'getRuleID'); $actions = id(new HeraldAction())->loadAllWhere( 'ruleID IN (%Ld)', $rule_ids); $actions = mgroup($actions, 'getRuleID'); foreach ($rules as $rule) { $rule->attachActions(idx($actions, $rule->getID(), array())); $rule->attachConditions(idx($conditions, $rule->getID(), array())); } } if ($this->needAppliedToPHIDs) { $conn_r = id(new HeraldRule())->establishConnection('r'); $applied = queryfx_all( $conn_r, 'SELECT * FROM %T WHERE ruleID IN (%Ld) AND phid IN (%Ls)', HeraldRule::TABLE_RULE_APPLIED, $rule_ids, $this->needAppliedToPHIDs); $map = array(); foreach ($applied as $row) { $map[$row['ruleID']][$row['phid']] = true; } foreach ($rules as $rule) { foreach ($this->needAppliedToPHIDs as $phid) { $rule->setRuleApplied( $phid, isset($map[$rule->getID()][$phid])); } } } $object_phids = array(); foreach ($rules as $rule) { if ($rule->isObjectRule()) { $object_phids[] = $rule->getTriggerObjectPHID(); } } if ($object_phids) { $objects = id(new PhabricatorObjectQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($object_phids) ->execute(); $objects = mpull($objects, null, 'getPHID'); } else { $objects = array(); } foreach ($rules as $key => $rule) { if ($rule->isObjectRule()) { $object = idx($objects, $rule->getTriggerObjectPHID()); if (!$object) { unset($rules[$key]); continue; } $rule->attachTriggerObject($object); } } return $rules; } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'rule.id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'rule.phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs) { $where[] = qsprintf( $conn_r, 'rule.authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->ruleTypes) { $where[] = qsprintf( $conn_r, 'rule.ruleType IN (%Ls)', $this->ruleTypes); } if ($this->contentTypes) { $where[] = qsprintf( $conn_r, 'rule.contentType IN (%Ls)', $this->contentTypes); } if ($this->disabled !== null) { $where[] = qsprintf( $conn_r, 'rule.isDisabled = %d', (int)$this->disabled); } if ($this->triggerObjectPHIDs) { $where[] = qsprintf( $conn_r, 'rule.triggerObjectPHID IN (%Ls)', $this->triggerObjectPHIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } private function validateRuleAuthors(array $rules) { // "Global" and "Object" rules always have valid authors. foreach ($rules as $key => $rule) { if ($rule->isGlobalRule() || $rule->isObjectRule()) { $rule->attachValidAuthor(true); unset($rules[$key]); continue; } } if (!$rules) { return; } // For personal rules, the author needs to exist and not be disabled. $user_phids = mpull($rules, 'getAuthorPHID'); $users = id(new PhabricatorPeopleQuery()) ->setViewer($this->getViewer()) ->withPHIDs($user_phids) ->execute(); $users = mpull($users, null, 'getPHID'); foreach ($rules as $key => $rule) { $author_phid = $rule->getAuthorPHID(); if (empty($users[$author_phid])) { $rule->attachValidAuthor(false); continue; } if (!$users[$author_phid]->isUserActivated()) { $rule->attachValidAuthor(false); continue; } $rule->attachValidAuthor(true); $rule->attachAuthor($users[$author_phid]); } } public function getQueryApplicationClass() { return 'PhabricatorHeraldApplication'; } } diff --git a/src/applications/herald/query/HeraldTranscriptQuery.php b/src/applications/herald/query/HeraldTranscriptQuery.php index b76b657a7f..d679009c4e 100644 --- a/src/applications/herald/query/HeraldTranscriptQuery.php +++ b/src/applications/herald/query/HeraldTranscriptQuery.php @@ -1,114 +1,114 @@ ids = $ids; return $this; } public function withObjectPHIDs(array $phids) { $this->objectPHIDs = $phids; return $this; } public function needPartialRecords($need_partial) { $this->needPartialRecords = $need_partial; return $this; } protected function loadPage() { $transcript = new HeraldTranscript(); $conn_r = $transcript->establishConnection('r'); // NOTE: Transcripts include a potentially enormous amount of serialized // data, so we're loading only some of the fields here if the caller asked // for partial records. if ($this->needPartialRecords) { $fields = implode( ', ', array( 'id', 'phid', 'objectPHID', 'time', 'duration', 'dryRun', 'host', )); } else { $fields = '*'; } $rows = queryfx_all( $conn_r, 'SELECT %Q FROM %T t %Q %Q %Q', $fields, $transcript->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $transcripts = $transcript->loadAllFromArray($rows); if ($this->needPartialRecords) { // Make sure nothing tries to write these; they aren't complete. foreach ($transcripts as $transcript) { $transcript->makeEphemeral(); } } return $transcripts; } protected function willFilterPage(array $transcripts) { $phids = mpull($transcripts, 'getObjectPHID'); $objects = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($phids) ->execute(); foreach ($transcripts as $key => $transcript) { if (empty($objects[$transcript->getObjectPHID()])) { $this->didRejectResult($transcript); unset($transcripts[$key]); } } return $transcripts; } - public function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->objectPHIDs) { $where[] = qsprintf( $conn_r, 'objectPHID in (%Ls)', $this->objectPHIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorHeraldApplication'; } } diff --git a/src/applications/legalpad/query/LegalpadDocumentQuery.php b/src/applications/legalpad/query/LegalpadDocumentQuery.php index 03ec6b3d3c..e2e43e0e28 100644 --- a/src/applications/legalpad/query/LegalpadDocumentQuery.php +++ b/src/applications/legalpad/query/LegalpadDocumentQuery.php @@ -1,278 +1,278 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withCreatorPHIDs(array $phids) { $this->creatorPHIDs = $phids; return $this; } public function withContributorPHIDs(array $phids) { $this->contributorPHIDs = $phids; return $this; } public function withSignerPHIDs(array $phids) { $this->signerPHIDs = $phids; return $this; } public function withSignatureRequired($bool) { $this->signatureRequired = $bool; return $this; } public function needDocumentBodies($need_bodies) { $this->needDocumentBodies = $need_bodies; return $this; } public function needContributors($need_contributors) { $this->needContributors = $need_contributors; return $this; } public function needSignatures($need_signatures) { $this->needSignatures = $need_signatures; return $this; } public function withDateCreatedBefore($date_created_before) { $this->dateCreatedBefore = $date_created_before; return $this; } public function withDateCreatedAfter($date_created_after) { $this->dateCreatedAfter = $date_created_after; return $this; } public function needViewerSignatures($need) { $this->needViewerSignatures = $need; return $this; } protected function loadPage() { $table = new LegalpadDocument(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT d.* FROM %T d %Q %Q %Q %Q %Q', $table->getTableName(), $this->buildJoinClause($conn_r), $this->buildWhereClause($conn_r), $this->buildGroupClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $documents = $table->loadAllFromArray($data); return $documents; } protected function willFilterPage(array $documents) { if ($this->needDocumentBodies) { $documents = $this->loadDocumentBodies($documents); } if ($this->needContributors) { $documents = $this->loadContributors($documents); } if ($this->needSignatures) { $documents = $this->loadSignatures($documents); } if ($this->needViewerSignatures) { if ($documents) { if ($this->getViewer()->getPHID()) { $signatures = id(new LegalpadDocumentSignatureQuery()) ->setViewer($this->getViewer()) ->withSignerPHIDs(array($this->getViewer()->getPHID())) ->withDocumentPHIDs(mpull($documents, 'getPHID')) ->execute(); $signatures = mpull($signatures, null, 'getDocumentPHID'); } else { $signatures = array(); } foreach ($documents as $document) { $signature = idx($signatures, $document->getPHID()); $document->attachUserSignature( $this->getViewer()->getPHID(), $signature); } } } return $documents; } private function buildJoinClause($conn_r) { $joins = array(); if ($this->contributorPHIDs !== null) { $joins[] = qsprintf( $conn_r, 'JOIN edge contributor ON contributor.src = d.phid AND contributor.type = %d', PhabricatorObjectHasContributorEdgeType::EDGECONST); } if ($this->signerPHIDs !== null) { $joins[] = qsprintf( $conn_r, 'JOIN %T signer ON signer.documentPHID = d.phid AND signer.signerPHID IN (%Ls)', id(new LegalpadDocumentSignature())->getTableName(), $this->signerPHIDs); } return implode(' ', $joins); } private function buildGroupClause(AphrontDatabaseConnection $conn_r) { if ($this->contributorPHIDs || $this->signerPHIDs) { return 'GROUP BY d.id'; } else { return ''; } } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'd.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'd.phid IN (%Ls)', $this->phids); } if ($this->creatorPHIDs !== null) { $where[] = qsprintf( $conn_r, 'd.creatorPHID IN (%Ls)', $this->creatorPHIDs); } if ($this->dateCreatedAfter !== null) { $where[] = qsprintf( $conn_r, 'd.dateCreated >= %d', $this->dateCreatedAfter); } if ($this->dateCreatedBefore !== null) { $where[] = qsprintf( $conn_r, 'd.dateCreated <= %d', $this->dateCreatedBefore); } if ($this->contributorPHIDs !== null) { $where[] = qsprintf( $conn_r, 'contributor.dst IN (%Ls)', $this->contributorPHIDs); } if ($this->signatureRequired !== null) { $where[] = qsprintf( $conn_r, 'd.requireSignature = %d', $this->signatureRequired); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } private function loadDocumentBodies(array $documents) { $body_phids = mpull($documents, 'getDocumentBodyPHID'); $bodies = id(new LegalpadDocumentBody())->loadAllWhere( 'phid IN (%Ls)', $body_phids); $bodies = mpull($bodies, null, 'getPHID'); foreach ($documents as $document) { $body = idx($bodies, $document->getDocumentBodyPHID()); $document->attachDocumentBody($body); } return $documents; } private function loadContributors(array $documents) { $document_map = mpull($documents, null, 'getPHID'); $edge_type = PhabricatorObjectHasContributorEdgeType::EDGECONST; $contributor_data = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(array_keys($document_map)) ->withEdgeTypes(array($edge_type)) ->execute(); foreach ($document_map as $document_phid => $document) { $data = $contributor_data[$document_phid]; $contributors = array_keys(idx($data, $edge_type, array())); $document->attachContributors($contributors); } return $documents; } private function loadSignatures(array $documents) { $document_map = mpull($documents, null, 'getPHID'); $signatures = id(new LegalpadDocumentSignatureQuery()) ->setViewer($this->getViewer()) ->withDocumentPHIDs(array_keys($document_map)) ->execute(); $signatures = mgroup($signatures, 'getDocumentPHID'); foreach ($documents as $document) { $sigs = idx($signatures, $document->getPHID(), array()); $document->attachSignatures($sigs); } return $documents; } public function getQueryApplicationClass() { return 'PhabricatorLegalpadApplication'; } } diff --git a/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php b/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php index 3bb3628c99..452ea6759b 100644 --- a/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php +++ b/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php @@ -1,150 +1,150 @@ ids = $ids; return $this; } public function withDocumentPHIDs(array $phids) { $this->documentPHIDs = $phids; return $this; } public function withSignerPHIDs(array $phids) { $this->signerPHIDs = $phids; return $this; } public function withDocumentVersions(array $versions) { $this->documentVersions = $versions; return $this; } public function withSecretKeys(array $keys) { $this->secretKeys = $keys; return $this; } public function withNameContains($text) { $this->nameContains = $text; return $this; } public function withEmailContains($text) { $this->emailContains = $text; return $this; } protected function loadPage() { $table = new LegalpadDocumentSignature(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $signatures = $table->loadAllFromArray($data); return $signatures; } protected function willFilterPage(array $signatures) { $document_phids = mpull($signatures, 'getDocumentPHID'); $documents = id(new LegalpadDocumentQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($document_phids) ->execute(); $documents = mpull($documents, null, 'getPHID'); foreach ($signatures as $key => $signature) { $document_phid = $signature->getDocumentPHID(); $document = idx($documents, $document_phid); if ($document) { $signature->attachDocument($document); } else { unset($signatures[$key]); } } return $signatures; } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); $where[] = $this->buildPagingClause($conn_r); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->documentPHIDs !== null) { $where[] = qsprintf( $conn_r, 'documentPHID IN (%Ls)', $this->documentPHIDs); } if ($this->signerPHIDs !== null) { $where[] = qsprintf( $conn_r, 'signerPHID IN (%Ls)', $this->signerPHIDs); } if ($this->documentVersions !== null) { $where[] = qsprintf( $conn_r, 'documentVersion IN (%Ld)', $this->documentVersions); } if ($this->secretKeys !== null) { $where[] = qsprintf( $conn_r, 'secretKey IN (%Ls)', $this->secretKeys); } if ($this->nameContains !== null) { $where[] = qsprintf( $conn_r, 'signerName LIKE %~', $this->nameContains); } if ($this->emailContains !== null) { $where[] = qsprintf( $conn_r, 'signerEmail LIKE %~', $this->emailContains); } return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorLegalpadApplication'; } } diff --git a/src/applications/mailinglists/query/PhabricatorMailingListQuery.php b/src/applications/mailinglists/query/PhabricatorMailingListQuery.php index 44f54edbda..113bb4c80d 100644 --- a/src/applications/mailinglists/query/PhabricatorMailingListQuery.php +++ b/src/applications/mailinglists/query/PhabricatorMailingListQuery.php @@ -1,86 +1,86 @@ ids = $ids; return $this; } public function withPHIDs($phids) { $this->phids = $phids; return $this; } public function withEmails(array $emails) { $this->emails = $emails; return $this; } public function withNames(array $names) { $this->names = $names; return $this; } protected function loadPage() { $table = new PhabricatorMetaMTAMailingList(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->names) { $where[] = qsprintf( $conn_r, 'name IN (%Ls)', $this->names); } if ($this->emails) { $where[] = qsprintf( $conn_r, 'email IN (%Ls)', $this->emails); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorMailingListsApplication'; } } diff --git a/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailQuery.php b/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailQuery.php index d2ac2f17a6..b173224914 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailQuery.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailQuery.php @@ -1,125 +1,125 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAddresses(array $addresses) { $this->addresses = $addresses; return $this; } public function withAddressPrefix($prefix) { $this->addressPrefix = $prefix; return $this; } public function withApplicationPHIDs(array $phids) { $this->applicationPHIDs = $phids; return $this; } protected function loadPage() { $table = new PhabricatorMetaMTAApplicationEmail(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T appemail %Q %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildApplicationSearchGroupClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $app_emails) { $app_emails_map = mgroup($app_emails, 'getApplicationPHID'); $applications = id(new PhabricatorApplicationQuery()) ->setViewer($this->getViewer()) ->withPHIDs(array_keys($app_emails_map)) ->execute(); $applications = mpull($applications, null, 'getPHID'); foreach ($app_emails_map as $app_phid => $app_emails_group) { foreach ($app_emails_group as $app_email) { $application = idx($applications, $app_phid); if (!$application) { unset($app_emails[$app_phid]); continue; } $app_email->attachApplication($application); } } return $app_emails; } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->addresses !== null) { $where[] = qsprintf( $conn_r, 'appemail.address IN (%Ls)', $this->addresses); } if ($this->addressPrefix !== null) { $where[] = qsprintf( $conn_r, 'appemail.address LIKE %>', $this->addressPrefix); } if ($this->applicationPHIDs !== null) { $where[] = qsprintf( $conn_r, 'appemail.applicationPHID IN (%Ls)', $this->applicationPHIDs); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'appemail.phid IN (%Ls)', $this->phids); } if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'appemail.id IN (%Ld)', $this->ids); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } protected function getPrimaryTableAlias() { return 'appemail'; } public function getQueryApplicationClass() { return 'PhabricatorMetaMTAApplication'; } } diff --git a/src/applications/notification/query/PhabricatorNotificationQuery.php b/src/applications/notification/query/PhabricatorNotificationQuery.php index a1f8ac63c7..aa430a2518 100644 --- a/src/applications/notification/query/PhabricatorNotificationQuery.php +++ b/src/applications/notification/query/PhabricatorNotificationQuery.php @@ -1,114 +1,114 @@ userPHIDs = $user_phids; return $this; } public function withKeys(array $keys) { $this->keys = $keys; return $this; } /** * Filter results by read/unread status. Note that `true` means to return * only unread notifications, while `false` means to return only //read// * notifications. The default is `null`, which returns both. * * @param mixed True or false to filter results by read status. Null to remove * the filter. * @return this * @task config */ public function withUnread($unread) { $this->unread = $unread; return $this; } /* -( Query Execution )---------------------------------------------------- */ protected function loadPage() { $story_table = new PhabricatorFeedStoryData(); $notification_table = new PhabricatorFeedStoryNotification(); $conn = $story_table->establishConnection('r'); $data = queryfx_all( $conn, 'SELECT story.*, notif.hasViewed FROM %T notif JOIN %T story ON notif.chronologicalKey = story.chronologicalKey %Q ORDER BY notif.chronologicalKey DESC %Q', $notification_table->getTableName(), $story_table->getTableName(), $this->buildWhereClause($conn), $this->buildLimitClause($conn)); $viewed_map = ipull($data, 'hasViewed', 'chronologicalKey'); $stories = PhabricatorFeedStory::loadAllFromRows( $data, $this->getViewer()); foreach ($stories as $key => $story) { $story->setHasViewed($viewed_map[$key]); } return $stories; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->userPHIDs !== null) { $where[] = qsprintf( $conn_r, 'notif.userPHID IN (%Ls)', $this->userPHIDs); } if ($this->unread !== null) { $where[] = qsprintf( $conn_r, 'notif.hasViewed = %d', (int)!$this->unread); } if ($this->keys) { $where[] = qsprintf( $conn_r, 'notif.chronologicalKey IN (%Ls)', $this->keys); } return $this->formatWhereClause($where); } protected function getResultCursor($item) { return $item->getChronologicalKey(); } public function getQueryApplicationClass() { return 'PhabricatorNotificationsApplication'; } } diff --git a/src/applications/nuance/query/NuanceItemQuery.php b/src/applications/nuance/query/NuanceItemQuery.php index 5264c4b527..4b87f38a77 100644 --- a/src/applications/nuance/query/NuanceItemQuery.php +++ b/src/applications/nuance/query/NuanceItemQuery.php @@ -1,70 +1,70 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withSourceIDs($source_ids) { $this->sourceIDs = $source_ids; return $this; } protected function loadPage() { $table = new NuanceItem(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); $where[] = $this->buildPagingClause($conn_r); if ($this->sourceID) { $where[] = qsprintf( $conn_r, 'sourceID IN (%Ld)', $this->sourceIDs); } if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } return $this->formatWhereClause($where); } } diff --git a/src/applications/nuance/query/NuanceQueueQuery.php b/src/applications/nuance/query/NuanceQueueQuery.php index b4a067f01c..822a60c620 100644 --- a/src/applications/nuance/query/NuanceQueueQuery.php +++ b/src/applications/nuance/query/NuanceQueueQuery.php @@ -1,56 +1,56 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } protected function loadPage() { $table = new NuanceQueue(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); $where[] = $this->buildPagingClause($conn_r); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } return $this->formatWhereClause($where); } } diff --git a/src/applications/nuance/query/NuanceRequestorQuery.php b/src/applications/nuance/query/NuanceRequestorQuery.php index e23cc71343..91a9878b83 100644 --- a/src/applications/nuance/query/NuanceRequestorQuery.php +++ b/src/applications/nuance/query/NuanceRequestorQuery.php @@ -1,56 +1,56 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } protected function loadPage() { $table = new NuanceRequestor(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); $where[] = $this->buildPagingClause($conn_r); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } return $this->formatWhereClause($where); } } diff --git a/src/applications/nuance/query/NuanceSourceQuery.php b/src/applications/nuance/query/NuanceSourceQuery.php index 3a177a4e42..77421656e7 100644 --- a/src/applications/nuance/query/NuanceSourceQuery.php +++ b/src/applications/nuance/query/NuanceSourceQuery.php @@ -1,83 +1,83 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withCreatorPHIDs(array $phids) { $this->CreatorPHIDs = $phids; return $this; } public function withTypes($types) { $this->types = $types; return $this; } protected function loadPage() { $table = new NuanceSource(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); $where[] = $this->buildPagingClause($conn_r); if ($this->creatorPHIDs) { $where[] = qsprintf( $conn_r, 'creatorPHID IN (%Ls)', $this->creatorPHIDs); } if ($this->types) { $where[] = qsprintf( $conn_r, 'type IN (%Ld)', $this->types); } if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } return $this->formatWhereClause($where); } } diff --git a/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php b/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php index 754e3474ab..f19be14434 100644 --- a/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php +++ b/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php @@ -1,95 +1,95 @@ phids = $phids; return $this; } public function withUserPHIDs(array $phids) { $this->userPHIDs = $phids; return $this; } public function withClientPHIDs(array $phids) { $this->clientPHIDs = $phids; return $this; } protected function loadPage() { $table = new PhabricatorOAuthClientAuthorization(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T auth %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $authorizations) { $client_phids = mpull($authorizations, 'getClientPHID'); $clients = id(new PhabricatorOAuthServerClientQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($client_phids) ->execute(); $clients = mpull($clients, null, 'getPHID'); foreach ($authorizations as $key => $authorization) { $client = idx($clients, $authorization->getClientPHID()); if (!$client) { unset($authorizations[$key]); continue; } $authorization->attachClient($client); } return $authorizations; } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->userPHIDs) { $where[] = qsprintf( $conn_r, 'userPHID IN (%Ls)', $this->userPHIDs); } if ($this->clientPHIDs) { $where[] = qsprintf( $conn_r, 'clientPHID IN (%Ls)', $this->clientPHIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorOAuthServerApplication'; } } diff --git a/src/applications/oauthserver/query/PhabricatorOAuthServerClientQuery.php b/src/applications/oauthserver/query/PhabricatorOAuthServerClientQuery.php index bf22a2a701..b5da9ecd5f 100644 --- a/src/applications/oauthserver/query/PhabricatorOAuthServerClientQuery.php +++ b/src/applications/oauthserver/query/PhabricatorOAuthServerClientQuery.php @@ -1,73 +1,73 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withCreatorPHIDs(array $phids) { $this->creatorPHIDs = $phids; return $this; } protected function loadPage() { $table = new PhabricatorOAuthServerClient(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T client %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->creatorPHIDs) { $where[] = qsprintf( $conn_r, 'creatorPHID IN (%Ls)', $this->creatorPHIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorOAuthServerApplication'; } } diff --git a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php index bdfb6f5a4e..64640c3ab8 100644 --- a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php +++ b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php @@ -1,86 +1,86 @@ ownerPHIDs = $phids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } protected function loadPage() { $table = new PhabricatorOwnersPackage(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT p.* FROM %T p %Q %Q %Q %Q', $table->getTableName(), $this->buildJoinClause($conn_r), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } private function buildJoinClause(AphrontDatabaseConnection $conn_r) { $joins = array(); if ($this->ownerPHIDs) { $joins[] = qsprintf( $conn_r, 'JOIN %T o ON o.packageID = p.id', id(new PhabricatorOwnersOwner())->getTableName()); } return implode(' ', $joins); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->phids) { $where[] = qsprintf( $conn_r, 'p.phid IN (%Ls)', $this->phids); } if ($this->ownerPHIDs) { $base_phids = $this->ownerPHIDs; $query = new PhabricatorProjectQuery(); $query->setViewer($this->getViewer()); $query->withMemberPHIDs($base_phids); $projects = $query->execute(); $project_phids = mpull($projects, 'getPHID'); $all_phids = array_merge($base_phids, $project_phids); $where[] = qsprintf( $conn_r, 'o.userPHID IN (%Ls)', $all_phids); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorOwnersApplication'; } } diff --git a/src/applications/passphrase/query/PassphraseCredentialQuery.php b/src/applications/passphrase/query/PassphraseCredentialQuery.php index 29f661397d..9cae8b1f85 100644 --- a/src/applications/passphrase/query/PassphraseCredentialQuery.php +++ b/src/applications/passphrase/query/PassphraseCredentialQuery.php @@ -1,163 +1,163 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withCredentialTypes(array $credential_types) { $this->credentialTypes = $credential_types; return $this; } public function withProvidesTypes(array $provides_types) { $this->providesTypes = $provides_types; return $this; } public function withIsDestroyed($destroyed) { $this->isDestroyed = $destroyed; return $this; } public function withAllowConduit($allow_conduit) { $this->allowConduit = $allow_conduit; return $this; } public function withNameContains($name_contains) { $this->nameContains = $name_contains; return $this; } public function needSecrets($need_secrets) { $this->needSecrets = $need_secrets; return $this; } protected function loadPage() { $table = new PassphraseCredential(); $conn_r = $table->establishConnection('r'); $rows = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($rows); } protected function willFilterPage(array $page) { if ($this->needSecrets) { $secret_ids = mpull($page, 'getSecretID'); $secret_ids = array_filter($secret_ids); $secrets = array(); if ($secret_ids) { $secret_objects = id(new PassphraseSecret())->loadAllWhere( 'id IN (%Ld)', $secret_ids); foreach ($secret_objects as $secret) { $secret_data = $secret->getSecretData(); $secrets[$secret->getID()] = new PhutilOpaqueEnvelope($secret_data); } } foreach ($page as $key => $credential) { $secret_id = $credential->getSecretID(); if (!$secret_id) { $credential->attachSecret(null); } else if (isset($secrets[$secret_id])) { $credential->attachSecret($secrets[$secret_id]); } else { unset($page[$key]); } } } return $page; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); $where[] = $this->buildPagingClause($conn_r); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->credentialTypes) { $where[] = qsprintf( $conn_r, 'credentialType in (%Ls)', $this->credentialTypes); } if ($this->providesTypes) { $where[] = qsprintf( $conn_r, 'providesType IN (%Ls)', $this->providesTypes); } if ($this->isDestroyed !== null) { $where[] = qsprintf( $conn_r, 'isDestroyed = %d', (int)$this->isDestroyed); } if ($this->allowConduit !== null) { $where[] = qsprintf( $conn_r, 'allowConduit = %d', (int)$this->allowConduit); } if (strlen($this->nameContains)) { $where[] = qsprintf( $conn_r, 'name LIKE %~', $this->nameContains); } return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorPassphraseApplication'; } } diff --git a/src/applications/paste/query/PhabricatorPasteQuery.php b/src/applications/paste/query/PhabricatorPasteQuery.php index 35c3820311..ae74978510 100644 --- a/src/applications/paste/query/PhabricatorPasteQuery.php +++ b/src/applications/paste/query/PhabricatorPasteQuery.php @@ -1,252 +1,252 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs(array $phids) { $this->authorPHIDs = $phids; return $this; } public function withParentPHIDs(array $phids) { $this->parentPHIDs = $phids; return $this; } public function needContent($need_content) { $this->needContent = $need_content; return $this; } public function needRawContent($need_raw_content) { $this->needRawContent = $need_raw_content; return $this; } public function withLanguages(array $languages) { $this->includeNoLanguage = false; foreach ($languages as $key => $language) { if ($language === null) { $languages[$key] = ''; continue; } } $this->languages = $languages; return $this; } public function withDateCreatedBefore($date_created_before) { $this->dateCreatedBefore = $date_created_before; return $this; } public function withDateCreatedAfter($date_created_after) { $this->dateCreatedAfter = $date_created_after; return $this; } protected function loadPage() { $table = new PhabricatorPaste(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT paste.* FROM %T paste %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $pastes = $table->loadAllFromArray($data); return $pastes; } protected function didFilterPage(array $pastes) { if ($this->needRawContent) { $pastes = $this->loadRawContent($pastes); } if ($this->needContent) { $this->loadContent($pastes); } return $pastes; } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); $where[] = $this->buildPagingClause($conn_r); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs) { $where[] = qsprintf( $conn_r, 'authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->parentPHIDs) { $where[] = qsprintf( $conn_r, 'parentPHID IN (%Ls)', $this->parentPHIDs); } if ($this->languages) { $where[] = qsprintf( $conn_r, 'language IN (%Ls)', $this->languages); } if ($this->dateCreatedAfter) { $where[] = qsprintf( $conn_r, 'dateCreated >= %d', $this->dateCreatedAfter); } if ($this->dateCreatedBefore) { $where[] = qsprintf( $conn_r, 'dateCreated <= %d', $this->dateCreatedBefore); } return $this->formatWhereClause($where); } private function getContentCacheKey(PhabricatorPaste $paste) { return implode( ':', array( 'P'.$paste->getID(), $paste->getFilePHID(), $paste->getLanguage(), )); } private function loadRawContent(array $pastes) { $file_phids = mpull($pastes, 'getFilePHID'); $files = id(new PhabricatorFileQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($file_phids) ->execute(); $files = mpull($files, null, 'getPHID'); foreach ($pastes as $key => $paste) { $file = idx($files, $paste->getFilePHID()); if (!$file) { unset($pastes[$key]); continue; } try { $paste->attachRawContent($file->loadFileData()); } catch (Exception $ex) { // We can hit various sorts of file storage issues here. Just drop the // paste if the file is dead. unset($pastes[$key]); continue; } } return $pastes; } private function loadContent(array $pastes) { $cache = new PhabricatorKeyValueDatabaseCache(); $cache = new PhutilKeyValueCacheProfiler($cache); $cache->setProfiler(PhutilServiceProfiler::getInstance()); $keys = array(); foreach ($pastes as $paste) { $keys[] = $this->getContentCacheKey($paste); } $caches = $cache->getKeys($keys); $need_raw = array(); foreach ($pastes as $key => $paste) { $key = $this->getContentCacheKey($paste); if (isset($caches[$key])) { $paste->attachContent(phutil_safe_html($caches[$key])); } else { $need_raw[$key] = $paste; } } if (!$need_raw) { return; } $write_data = array(); $need_raw = $this->loadRawContent($need_raw); foreach ($need_raw as $key => $paste) { $content = $this->buildContent($paste); $paste->attachContent($content); $write_data[$this->getContentCacheKey($paste)] = (string)$content; } $cache->setKeys($write_data); } private function buildContent(PhabricatorPaste $paste) { $language = $paste->getLanguage(); $source = $paste->getRawContent(); if (empty($language)) { return PhabricatorSyntaxHighlighter::highlightWithFilename( $paste->getTitle(), $source); } else { return PhabricatorSyntaxHighlighter::highlightWithLanguage( $language, $source); } } public function getQueryApplicationClass() { return 'PhabricatorPasteApplication'; } } diff --git a/src/applications/people/query/PhabricatorPeopleLogQuery.php b/src/applications/people/query/PhabricatorPeopleLogQuery.php index 3315234d12..9bcdc53f49 100644 --- a/src/applications/people/query/PhabricatorPeopleLogQuery.php +++ b/src/applications/people/query/PhabricatorPeopleLogQuery.php @@ -1,113 +1,113 @@ actorPHIDs = $actor_phids; return $this; } public function withUserPHIDs(array $user_phids) { $this->userPHIDs = $user_phids; return $this; } public function withRelatedPHIDs(array $related_phids) { $this->relatedPHIDs = $related_phids; return $this; } public function withSessionKeys(array $session_keys) { $this->sessionKeys = $session_keys; return $this; } public function withActions(array $actions) { $this->actions = $actions; return $this; } public function withRemoteAddressPrefix($remote_address_prefix) { $this->remoteAddressPrefix = $remote_address_prefix; return $this; } protected function loadPage() { $table = new PhabricatorUserLog(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->actorPHIDs !== null) { $where[] = qsprintf( $conn_r, 'actorPHID IN (%Ls)', $this->actorPHIDs); } if ($this->userPHIDs !== null) { $where[] = qsprintf( $conn_r, 'userPHID IN (%Ls)', $this->userPHIDs); } if ($this->relatedPHIDs !== null) { $where[] = qsprintf( $conn_r, 'actorPHID IN (%Ls) OR userPHID IN (%Ls)', $this->relatedPHIDs, $this->relatedPHIDs); } if ($this->sessionKeys !== null) { $where[] = qsprintf( $conn_r, 'session IN (%Ls)', $this->sessionKeys); } if ($this->actions !== null) { $where[] = qsprintf( $conn_r, 'action IN (%Ls)', $this->actions); } if ($this->remoteAddressPrefix !== null) { $where[] = qsprintf( $conn_r, 'remoteAddr LIKE %>', $this->remoteAddressPrefix); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorPeopleApplication'; } } diff --git a/src/applications/people/query/PhabricatorPeopleQuery.php b/src/applications/people/query/PhabricatorPeopleQuery.php index 472e76a8a1..437f764fe9 100644 --- a/src/applications/people/query/PhabricatorPeopleQuery.php +++ b/src/applications/people/query/PhabricatorPeopleQuery.php @@ -1,350 +1,350 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withEmails(array $emails) { $this->emails = $emails; return $this; } public function withRealnames(array $realnames) { $this->realnames = $realnames; return $this; } public function withUsernames(array $usernames) { $this->usernames = $usernames; return $this; } public function withDateCreatedBefore($date_created_before) { $this->dateCreatedBefore = $date_created_before; return $this; } public function withDateCreatedAfter($date_created_after) { $this->dateCreatedAfter = $date_created_after; return $this; } public function withIsAdmin($admin) { $this->isAdmin = $admin; return $this; } public function withIsSystemAgent($system_agent) { $this->isSystemAgent = $system_agent; return $this; } public function withIsDisabled($disabled) { $this->isDisabled = $disabled; return $this; } public function withIsApproved($approved) { $this->isApproved = $approved; return $this; } public function withNameLike($like) { $this->nameLike = $like; return $this; } public function withNameTokens(array $tokens) { $this->nameTokens = array_values($tokens); return $this; } public function needPrimaryEmail($need) { $this->needPrimaryEmail = $need; return $this; } public function needProfile($need) { $this->needProfile = $need; return $this; } public function needProfileImage($need) { $this->needProfileImage = $need; return $this; } public function needStatus($need) { $this->needStatus = $need; return $this; } protected function loadPage() { $table = new PhabricatorUser(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T user %Q %Q %Q %Q %Q', $table->getTableName(), $this->buildJoinsClause($conn_r), $this->buildWhereClause($conn_r), $this->buildGroupClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); if ($this->needPrimaryEmail) { $table->putInSet(new LiskDAOSet()); } return $table->loadAllFromArray($data); } protected function didFilterPage(array $users) { if ($this->needProfile) { $user_list = mpull($users, null, 'getPHID'); $profiles = new PhabricatorUserProfile(); $profiles = $profiles->loadAllWhere('userPHID IN (%Ls)', array_keys($user_list)); $profiles = mpull($profiles, null, 'getUserPHID'); foreach ($user_list as $user_phid => $user) { $profile = idx($profiles, $user_phid); if (!$profile) { $profile = new PhabricatorUserProfile(); $profile->setUserPHID($user_phid); } $user->attachUserProfile($profile); } } if ($this->needProfileImage) { $user_profile_file_phids = mpull($users, 'getProfileImagePHID'); $user_profile_file_phids = array_filter($user_profile_file_phids); if ($user_profile_file_phids) { $files = id(new PhabricatorFileQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($user_profile_file_phids) ->execute(); $files = mpull($files, null, 'getPHID'); } else { $files = array(); } foreach ($users as $user) { $image_phid = $user->getProfileImagePHID(); if (isset($files[$image_phid])) { $profile_image_uri = $files[$image_phid]->getBestURI(); } else { $profile_image_uri = PhabricatorUser::getDefaultProfileImageURI(); } $user->attachProfileImageURI($profile_image_uri); } } if ($this->needStatus) { $user_list = mpull($users, null, 'getPHID'); $statuses = id(new PhabricatorCalendarEvent())->loadCurrentStatuses( array_keys($user_list)); foreach ($user_list as $phid => $user) { $status = idx($statuses, $phid); if ($status) { $user->attachStatus($status); } } } return $users; } private function buildGroupClause(AphrontDatabaseConnection $conn) { if ($this->nameTokens) { return qsprintf( $conn, 'GROUP BY user.id'); } else { return $this->buildApplicationSearchGroupClause($conn); } } private function buildJoinsClause($conn_r) { $joins = array(); if ($this->emails) { $email_table = new PhabricatorUserEmail(); $joins[] = qsprintf( $conn_r, 'JOIN %T email ON email.userPHID = user.PHID', $email_table->getTableName()); } if ($this->nameTokens) { foreach ($this->nameTokens as $key => $token) { $token_table = 'token_'.$key; $joins[] = qsprintf( $conn_r, 'JOIN %T %T ON %T.userID = user.id AND %T.token LIKE %>', PhabricatorUser::NAMETOKEN_TABLE, $token_table, $token_table, $token_table, $token); } } $joins[] = $this->buildApplicationSearchJoinClause($conn_r); $joins = implode(' ', $joins); return $joins; } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->usernames !== null) { $where[] = qsprintf( $conn_r, 'user.userName IN (%Ls)', $this->usernames); } if ($this->emails !== null) { $where[] = qsprintf( $conn_r, 'email.address IN (%Ls)', $this->emails); } if ($this->realnames !== null) { $where[] = qsprintf( $conn_r, 'user.realName IN (%Ls)', $this->realnames); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'user.phid IN (%Ls)', $this->phids); } if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'user.id IN (%Ld)', $this->ids); } if ($this->dateCreatedAfter) { $where[] = qsprintf( $conn_r, 'user.dateCreated >= %d', $this->dateCreatedAfter); } if ($this->dateCreatedBefore) { $where[] = qsprintf( $conn_r, 'user.dateCreated <= %d', $this->dateCreatedBefore); } if ($this->isAdmin) { $where[] = qsprintf( $conn_r, 'user.isAdmin = 1'); } if ($this->isDisabled !== null) { $where[] = qsprintf( $conn_r, 'user.isDisabled = %d', (int)$this->isDisabled); } if ($this->isApproved !== null) { $where[] = qsprintf( $conn_r, 'user.isApproved = %d', (int)$this->isApproved); } if ($this->isSystemAgent) { $where[] = qsprintf( $conn_r, 'user.isSystemAgent = 1'); } if (strlen($this->nameLike)) { $where[] = qsprintf( $conn_r, 'user.username LIKE %~ OR user.realname LIKE %~', $this->nameLike, $this->nameLike); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } protected function getPrimaryTableAlias() { return 'user'; } public function getQueryApplicationClass() { return 'PhabricatorPeopleApplication'; } public function getOrderableColumns() { return parent::getOrderableColumns() + array( 'username' => array( 'table' => 'user', 'column' => 'username', 'type' => 'string', 'reverse' => true, 'unique' => true, ), ); } protected function getPagingValueMap($cursor, array $keys) { $user = $this->loadCursorObject($cursor); return array( 'id' => $user->getID(), 'username' => $user->getUsername(), ); } } diff --git a/src/applications/phame/query/PhameBlogQuery.php b/src/applications/phame/query/PhameBlogQuery.php index 160749a18f..a3e88e5143 100644 --- a/src/applications/phame/query/PhameBlogQuery.php +++ b/src/applications/phame/query/PhameBlogQuery.php @@ -1,80 +1,80 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withDomain($domain) { $this->domain = $domain; return $this; } protected function loadPage() { $table = new PhameBlog(); $conn_r = $table->establishConnection('r'); $where_clause = $this->buildWhereClause($conn_r); $order_clause = $this->buildOrderClause($conn_r); $limit_clause = $this->buildLimitClause($conn_r); $data = queryfx_all( $conn_r, 'SELECT * FROM %T b %Q %Q %Q', $table->getTableName(), $where_clause, $order_clause, $limit_clause); $blogs = $table->loadAllFromArray($data); return $blogs; } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ls)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->domain) { $where[] = qsprintf( $conn_r, 'domain = %s', $this->domain); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { // TODO: Can we set this without breaking public blogs? return null; } } diff --git a/src/applications/phame/query/PhamePostQuery.php b/src/applications/phame/query/PhamePostQuery.php index d87c26e35e..2eed8d2960 100644 --- a/src/applications/phame/query/PhamePostQuery.php +++ b/src/applications/phame/query/PhamePostQuery.php @@ -1,146 +1,146 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withBloggerPHIDs(array $blogger_phids) { $this->bloggerPHIDs = $blogger_phids; return $this; } public function withBlogPHIDs(array $blog_phids) { $this->blogPHIDs = $blog_phids; return $this; } public function withPhameTitles(array $phame_titles) { $this->phameTitles = $phame_titles; return $this; } public function withVisibility($visibility) { $this->visibility = $visibility; return $this; } public function withPublishedAfter($time) { $this->publishedAfter = $time; return $this; } protected function loadPage() { $table = new PhamePost(); $conn_r = $table->establishConnection('r'); $where_clause = $this->buildWhereClause($conn_r); $order_clause = $this->buildOrderClause($conn_r); $limit_clause = $this->buildLimitClause($conn_r); $data = queryfx_all( $conn_r, 'SELECT * FROM %T p %Q %Q %Q', $table->getTableName(), $where_clause, $order_clause, $limit_clause); $posts = $table->loadAllFromArray($data); if ($posts) { // We require these to do visibility checks, so load them unconditionally. $blog_phids = mpull($posts, 'getBlogPHID'); $blogs = id(new PhameBlogQuery()) ->setViewer($this->getViewer()) ->withPHIDs($blog_phids) ->execute(); $blogs = mpull($blogs, null, 'getPHID'); foreach ($posts as $post) { if (isset($blogs[$post->getBlogPHID()])) { $post->setBlog($blogs[$post->getBlogPHID()]); } } } return $posts; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'p.id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'p.phid IN (%Ls)', $this->phids); } if ($this->bloggerPHIDs) { $where[] = qsprintf( $conn_r, 'p.bloggerPHID IN (%Ls)', $this->bloggerPHIDs); } if ($this->phameTitles) { $where[] = qsprintf( $conn_r, 'p.phameTitle IN (%Ls)', $this->phameTitles); } if ($this->visibility !== null) { $where[] = qsprintf( $conn_r, 'p.visibility = %d', $this->visibility); } if ($this->publishedAfter !== null) { $where[] = qsprintf( $conn_r, 'p.datePublished > %d', $this->publishedAfter); } if ($this->blogPHIDs) { $where[] = qsprintf( $conn_r, 'p.blogPHID in (%Ls)', $this->blogPHIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { // TODO: Does setting this break public blogs? return null; } } diff --git a/src/applications/phlux/query/PhluxVariableQuery.php b/src/applications/phlux/query/PhluxVariableQuery.php index 3439ee1d8b..82072485d4 100644 --- a/src/applications/phlux/query/PhluxVariableQuery.php +++ b/src/applications/phlux/query/PhluxVariableQuery.php @@ -1,95 +1,95 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withKeys(array $keys) { $this->keys = $keys; return $this; } protected function loadPage() { $table = new PhluxVariable(); $conn_r = $table->establishConnection('r'); $rows = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($rows); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->keys !== null) { $where[] = qsprintf( $conn_r, 'variableKey IN (%Ls)', $this->keys); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } protected function getDefaultOrderVector() { return array('key'); } public function getOrderableColumns() { return array( 'key' => array( 'column' => 'variableKey', 'type' => 'string', 'reverse' => true, 'unique' => true, ), ); } protected function getPagingValueMap($cursor, array $keys) { $object = $this->loadCursorObject($cursor); return array( 'key' => $object->getVariableKey(), ); } public function getQueryApplicationClass() { return 'PhabricatorPhluxApplication'; } } diff --git a/src/applications/pholio/query/PholioImageQuery.php b/src/applications/pholio/query/PholioImageQuery.php index dd5fb23f3c..2d5057ccdc 100644 --- a/src/applications/pholio/query/PholioImageQuery.php +++ b/src/applications/pholio/query/PholioImageQuery.php @@ -1,165 +1,165 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withMockIDs(array $mock_ids) { $this->mockIDs = $mock_ids; return $this; } public function withObsolete($obsolete) { $this->obsolete = $obsolete; return $this; } public function needInlineComments($need_inline_comments) { $this->needInlineComments = $need_inline_comments; return $this; } public function setMockCache($mock_cache) { $this->mockCache = $mock_cache; return $this; } public function getMockCache() { return $this->mockCache; } protected function loadPage() { $table = new PholioImage(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $images = $table->loadAllFromArray($data); return $images; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); $where[] = $this->buildPagingClause($conn_r); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->mockIDs) { $where[] = qsprintf( $conn_r, 'mockID IN (%Ld)', $this->mockIDs); } if ($this->obsolete !== null) { $where[] = qsprintf( $conn_r, 'isObsolete = %d', $this->obsolete); } return $this->formatWhereClause($where); } protected function willFilterPage(array $images) { assert_instances_of($images, 'PholioImage'); if ($this->getMockCache()) { $mocks = $this->getMockCache(); } else { $mock_ids = mpull($images, 'getMockID'); // DO NOT set needImages to true; recursion results! $mocks = id(new PholioMockQuery()) ->setViewer($this->getViewer()) ->withIDs($mock_ids) ->execute(); $mocks = mpull($mocks, null, 'getID'); } foreach ($images as $index => $image) { $mock = idx($mocks, $image->getMockID()); if ($mock) { $image->attachMock($mock); } else { // mock is missing or we can't see it unset($images[$index]); } } return $images; } protected function didFilterPage(array $images) { assert_instances_of($images, 'PholioImage'); $file_phids = mpull($images, 'getFilePHID'); $all_files = id(new PhabricatorFileQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($file_phids) ->execute(); $all_files = mpull($all_files, null, 'getPHID'); if ($this->needInlineComments) { $all_inline_comments = id(new PholioTransactionComment()) ->loadAllWhere('imageid IN (%Ld)', mpull($images, 'getID')); $all_inline_comments = mgroup($all_inline_comments, 'getImageID'); } foreach ($images as $image) { $file = idx($all_files, $image->getFilePHID()); if (!$file) { $file = PhabricatorFile::loadBuiltin($this->getViewer(), 'missing.png'); } $image->attachFile($file); if ($this->needInlineComments) { $inlines = idx($all_inline_comments, $image->getID(), array()); $image->attachInlineComments($inlines); } } return $images; } public function getQueryApplicationClass() { return 'PhabricatorPholioApplication'; } } diff --git a/src/applications/pholio/query/PholioMockQuery.php b/src/applications/pholio/query/PholioMockQuery.php index d80bf57a28..2f6edd313f 100644 --- a/src/applications/pholio/query/PholioMockQuery.php +++ b/src/applications/pholio/query/PholioMockQuery.php @@ -1,181 +1,181 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs(array $author_phids) { $this->authorPHIDs = $author_phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function needCoverFiles($need_cover_files) { $this->needCoverFiles = $need_cover_files; return $this; } public function needImages($need_images) { $this->needImages = $need_images; return $this; } public function needInlineComments($need_inline_comments) { $this->needInlineComments = $need_inline_comments; return $this; } public function needTokenCounts($need) { $this->needTokenCounts = $need; return $this; } protected function loadPage() { $table = new PholioMock(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $mocks = $table->loadAllFromArray($data); if ($mocks && $this->needImages) { self::loadImages($this->getViewer(), $mocks, $this->needInlineComments); } if ($mocks && $this->needCoverFiles) { $this->loadCoverFiles($mocks); } if ($mocks && $this->needTokenCounts) { $this->loadTokenCounts($mocks); } return $mocks; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); $where[] = $this->buildPagingClause($conn_r); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs) { $where[] = qsprintf( $conn_r, 'authorPHID in (%Ls)', $this->authorPHIDs); } if ($this->statuses) { $where[] = qsprintf( $conn_r, 'status IN (%Ls)', $this->statuses); } return $this->formatWhereClause($where); } public static function loadImages( PhabricatorUser $viewer, array $mocks, $need_inline_comments) { assert_instances_of($mocks, 'PholioMock'); $mock_map = mpull($mocks, null, 'getID'); $all_images = id(new PholioImageQuery()) ->setViewer($viewer) ->setMockCache($mock_map) ->withMockIDs(array_keys($mock_map)) ->needInlineComments($need_inline_comments) ->execute(); $image_groups = mgroup($all_images, 'getMockID'); foreach ($mocks as $mock) { $mock_images = idx($image_groups, $mock->getID(), array()); $mock->attachAllImages($mock_images); $active_images = mfilter($mock_images, 'getIsObsolete', true); $mock->attachImages(msort($active_images, 'getSequence')); } } private function loadCoverFiles(array $mocks) { assert_instances_of($mocks, 'PholioMock'); $cover_file_phids = mpull($mocks, 'getCoverPHID'); $cover_files = id(new PhabricatorFileQuery()) ->setViewer($this->getViewer()) ->withPHIDs($cover_file_phids) ->execute(); $cover_files = mpull($cover_files, null, 'getPHID'); foreach ($mocks as $mock) { $file = idx($cover_files, $mock->getCoverPHID()); if (!$file) { $file = PhabricatorFile::loadBuiltin($this->getViewer(), 'missing.png'); } $mock->attachCoverFile($file); } } private function loadTokenCounts(array $mocks) { assert_instances_of($mocks, 'PholioMock'); $phids = mpull($mocks, 'getPHID'); $counts = id(new PhabricatorTokenCountQuery()) ->withObjectPHIDs($phids) ->execute(); foreach ($mocks as $mock) { $mock->attachTokenCount(idx($counts, $mock->getPHID(), 0)); } } public function getQueryApplicationClass() { return 'PhabricatorPholioApplication'; } } diff --git a/src/applications/phortune/query/PhortuneAccountQuery.php b/src/applications/phortune/query/PhortuneAccountQuery.php index 676badcb0c..a567aa2d38 100644 --- a/src/applications/phortune/query/PhortuneAccountQuery.php +++ b/src/applications/phortune/query/PhortuneAccountQuery.php @@ -1,123 +1,123 @@ setViewer($user) ->withMemberPHIDs(array($user->getPHID())) ->execute(); if (!$accounts) { $accounts = array( PhortuneAccount::createNewAccount($user, $content_source), ); } $accounts = mpull($accounts, null, 'getPHID'); return $accounts; } public function withIDs(array $ids) { $this->ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withMemberPHIDs(array $phids) { $this->memberPHIDs = $phids; return $this; } protected function loadPage() { $table = new PhortuneAccount(); $conn = $table->establishConnection('r'); $rows = queryfx_all( $conn, 'SELECT a.* FROM %T a %Q %Q %Q %Q', $table->getTableName(), $this->buildJoinClause($conn), $this->buildWhereClause($conn), $this->buildOrderClause($conn), $this->buildLimitClause($conn)); return $table->loadAllFromArray($rows); } protected function willFilterPage(array $accounts) { $query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(mpull($accounts, 'getPHID')) ->withEdgeTypes(array(PhortuneAccountHasMemberEdgeType::EDGECONST)); $query->execute(); foreach ($accounts as $account) { $member_phids = $query->getDestinationPHIDs(array($account->getPHID())); $member_phids = array_reverse($member_phids); $account->attachMemberPHIDs($member_phids); } return $accounts; } - private function buildWhereClause(AphrontDatabaseConnection $conn) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); $where[] = $this->buildPagingClause($conn); if ($this->ids) { $where[] = qsprintf( $conn, 'a.id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn, 'a.phid IN (%Ls)', $this->phids); } if ($this->memberPHIDs) { $where[] = qsprintf( $conn, 'm.dst IN (%Ls)', $this->memberPHIDs); } return $this->formatWhereClause($where); } private function buildJoinClause(AphrontDatabaseConnection $conn) { $joins = array(); if ($this->memberPHIDs) { $joins[] = qsprintf( $conn, 'LEFT JOIN %T m ON a.phid = m.src AND m.type = %d', PhabricatorEdgeConfig::TABLE_NAME_EDGE, PhortuneAccountHasMemberEdgeType::EDGECONST); } return implode(' ', $joins); } public function getQueryApplicationClass() { return 'PhabricatorPhortuneApplication'; } } diff --git a/src/applications/phortune/query/PhortuneCartQuery.php b/src/applications/phortune/query/PhortuneCartQuery.php index 4897ad7f22..5009d3a685 100644 --- a/src/applications/phortune/query/PhortuneCartQuery.php +++ b/src/applications/phortune/query/PhortuneCartQuery.php @@ -1,223 +1,223 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAccountPHIDs(array $account_phids) { $this->accountPHIDs = $account_phids; return $this; } public function withMerchantPHIDs(array $merchant_phids) { $this->merchantPHIDs = $merchant_phids; return $this; } public function withSubscriptionPHIDs(array $subscription_phids) { $this->subscriptionPHIDs = $subscription_phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } /** * Include or exclude carts which represent invoices with payments due. * * @param bool `true` to select invoices; `false` to exclude invoices. * @return this */ public function withInvoices($invoices) { $this->invoices = $invoices; return $this; } public function needPurchases($need_purchases) { $this->needPurchases = $need_purchases; return $this; } protected function loadPage() { $table = new PhortuneCart(); $conn = $table->establishConnection('r'); $rows = queryfx_all( $conn, 'SELECT cart.* FROM %T cart %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn), $this->buildOrderClause($conn), $this->buildLimitClause($conn)); return $table->loadAllFromArray($rows); } protected function willFilterPage(array $carts) { $accounts = id(new PhortuneAccountQuery()) ->setViewer($this->getViewer()) ->withPHIDs(mpull($carts, 'getAccountPHID')) ->execute(); $accounts = mpull($accounts, null, 'getPHID'); foreach ($carts as $key => $cart) { $account = idx($accounts, $cart->getAccountPHID()); if (!$account) { unset($carts[$key]); continue; } $cart->attachAccount($account); } if (!$carts) { return array(); } $merchants = id(new PhortuneMerchantQuery()) ->setViewer($this->getViewer()) ->withPHIDs(mpull($carts, 'getMerchantPHID')) ->execute(); $merchants = mpull($merchants, null, 'getPHID'); foreach ($carts as $key => $cart) { $merchant = idx($merchants, $cart->getMerchantPHID()); if (!$merchant) { unset($carts[$key]); continue; } $cart->attachMerchant($merchant); } if (!$carts) { return array(); } $implementations = array(); $cart_map = mgroup($carts, 'getCartClass'); foreach ($cart_map as $class => $class_carts) { $implementations += newv($class, array())->loadImplementationsForCarts( $this->getViewer(), $class_carts); } foreach ($carts as $key => $cart) { $implementation = idx($implementations, $key); if (!$implementation) { unset($carts[$key]); continue; } $cart->attachImplementation($implementation); } return $carts; } protected function didFilterPage(array $carts) { if ($this->needPurchases) { $purchases = id(new PhortunePurchaseQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withCartPHIDs(mpull($carts, 'getPHID')) ->execute(); $purchases = mgroup($purchases, 'getCartPHID'); foreach ($carts as $cart) { $cart->attachPurchases(idx($purchases, $cart->getPHID(), array())); } } return $carts; } - private function buildWhereClause(AphrontDatabaseConnection $conn) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); $where[] = $this->buildPagingClause($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'cart.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'cart.phid IN (%Ls)', $this->phids); } if ($this->accountPHIDs !== null) { $where[] = qsprintf( $conn, 'cart.accountPHID IN (%Ls)', $this->accountPHIDs); } if ($this->merchantPHIDs !== null) { $where[] = qsprintf( $conn, 'cart.merchantPHID IN (%Ls)', $this->merchantPHIDs); } if ($this->subscriptionPHIDs !== null) { $where[] = qsprintf( $conn, 'cart.subscriptionPHID IN (%Ls)', $this->subscriptionPHIDs); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'cart.status IN (%Ls)', $this->statuses); } if ($this->invoices !== null) { if ($this->invoices) { $where[] = qsprintf( $conn, 'cart.status = %s AND cart.isInvoice = 1', PhortuneCart::STATUS_READY); } else { $where[] = qsprintf( $conn, 'cart.status != %s OR cart.isInvoice = 0', PhortuneCart::STATUS_READY); } } return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorPhortuneApplication'; } } diff --git a/src/applications/phortune/query/PhortuneChargeQuery.php b/src/applications/phortune/query/PhortuneChargeQuery.php index aba655b4e4..6b11cbe95e 100644 --- a/src/applications/phortune/query/PhortuneChargeQuery.php +++ b/src/applications/phortune/query/PhortuneChargeQuery.php @@ -1,144 +1,144 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAccountPHIDs(array $account_phids) { $this->accountPHIDs = $account_phids; return $this; } public function withCartPHIDs(array $cart_phids) { $this->cartPHIDs = $cart_phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function needCarts($need_carts) { $this->needCarts = $need_carts; return $this; } protected function loadPage() { $table = new PhortuneCharge(); $conn = $table->establishConnection('r'); $rows = queryfx_all( $conn, 'SELECT charge.* FROM %T charge %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn), $this->buildOrderClause($conn), $this->buildLimitClause($conn)); return $table->loadAllFromArray($rows); } protected function willFilterPage(array $charges) { $accounts = id(new PhortuneAccountQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs(mpull($charges, 'getAccountPHID')) ->execute(); $accounts = mpull($accounts, null, 'getPHID'); foreach ($charges as $key => $charge) { $account = idx($accounts, $charge->getAccountPHID()); if (!$account) { unset($charges[$key]); continue; } $charge->attachAccount($account); } return $charges; } protected function didFilterPage(array $charges) { if ($this->needCarts) { $carts = id(new PhortuneCartQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs(mpull($charges, 'getCartPHID')) ->execute(); $carts = mpull($carts, null, 'getPHID'); foreach ($charges as $charge) { $cart = idx($carts, $charge->getCartPHID()); $charge->attachCart($cart); } } return $charges; } - private function buildWhereClause(AphrontDatabaseConnection $conn) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); $where[] = $this->buildPagingClause($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'charge.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'charge.phid IN (%Ls)', $this->phids); } if ($this->accountPHIDs !== null) { $where[] = qsprintf( $conn, 'charge.accountPHID IN (%Ls)', $this->accountPHIDs); } if ($this->cartPHIDs !== null) { $where[] = qsprintf( $conn, 'charge.cartPHID IN (%Ls)', $this->cartPHIDs); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'charge.status IN (%Ls)', $this->statuses); } return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorPhortuneApplication'; } } diff --git a/src/applications/phortune/query/PhortuneMerchantQuery.php b/src/applications/phortune/query/PhortuneMerchantQuery.php index 1632797421..41a6f1004d 100644 --- a/src/applications/phortune/query/PhortuneMerchantQuery.php +++ b/src/applications/phortune/query/PhortuneMerchantQuery.php @@ -1,103 +1,103 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withMemberPHIDs(array $member_phids) { $this->memberPHIDs = $member_phids; return $this; } protected function loadPage() { $table = new PhortuneMerchant(); $conn = $table->establishConnection('r'); $rows = queryfx_all( $conn, 'SELECT m.* FROM %T m %Q %Q %Q %Q', $table->getTableName(), $this->buildJoinClause($conn), $this->buildWhereClause($conn), $this->buildOrderClause($conn), $this->buildLimitClause($conn)); return $table->loadAllFromArray($rows); } protected function willFilterPage(array $merchants) { $query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(mpull($merchants, 'getPHID')) ->withEdgeTypes(array(PhortuneMerchantHasMemberEdgeType::EDGECONST)); $query->execute(); foreach ($merchants as $merchant) { $member_phids = $query->getDestinationPHIDs(array($merchant->getPHID())); $member_phids = array_reverse($member_phids); $merchant->attachMemberPHIDs($member_phids); } return $merchants; } - private function buildWhereClause(AphrontDatabaseConnection $conn) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->memberPHIDs !== null) { $where[] = qsprintf( $conn, 'e.dst IN (%Ls)', $this->memberPHIDs); } $where[] = $this->buildPagingClause($conn); return $this->formatWhereClause($where); } private function buildJoinClause(AphrontDatabaseConnection $conn) { $joins = array(); if ($this->memberPHIDs !== null) { $joins[] = qsprintf( $conn, 'LEFT JOIN %T e ON m.phid = e.src AND e.type = %d', PhabricatorEdgeConfig::TABLE_NAME_EDGE, PhortuneMerchantHasMemberEdgeType::EDGECONST); } return implode(' ', $joins); } public function getQueryApplicationClass() { return 'PhabricatorPhortuneApplication'; } } diff --git a/src/applications/phortune/query/PhortunePaymentMethodQuery.php b/src/applications/phortune/query/PhortunePaymentMethodQuery.php index c6b1fd878a..07d49a5909 100644 --- a/src/applications/phortune/query/PhortunePaymentMethodQuery.php +++ b/src/applications/phortune/query/PhortunePaymentMethodQuery.php @@ -1,156 +1,156 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAccountPHIDs(array $phids) { $this->accountPHIDs = $phids; return $this; } public function withMerchantPHIDs(array $phids) { $this->merchantPHIDs = $phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } protected function loadPage() { $table = new PhortunePaymentMethod(); $conn = $table->establishConnection('r'); $rows = queryfx_all( $conn, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn), $this->buildOrderClause($conn), $this->buildLimitClause($conn)); return $table->loadAllFromArray($rows); } protected function willFilterPage(array $methods) { $accounts = id(new PhortuneAccountQuery()) ->setViewer($this->getViewer()) ->withPHIDs(mpull($methods, 'getAccountPHID')) ->execute(); $accounts = mpull($accounts, null, 'getPHID'); foreach ($methods as $key => $method) { $account = idx($accounts, $method->getAccountPHID()); if (!$account) { unset($methods[$key]); continue; } $method->attachAccount($account); } if (!$methods) { return $methods; } $merchants = id(new PhortuneMerchantQuery()) ->setViewer($this->getViewer()) ->withPHIDs(mpull($methods, 'getMerchantPHID')) ->execute(); $merchants = mpull($merchants, null, 'getPHID'); foreach ($methods as $key => $method) { $merchant = idx($merchants, $method->getMerchantPHID()); if (!$merchant) { unset($methods[$key]); continue; } $method->attachMerchant($merchant); } if (!$methods) { return $methods; } $provider_configs = id(new PhortunePaymentProviderConfigQuery()) ->setViewer($this->getViewer()) ->withPHIDs(mpull($methods, 'getProviderPHID')) ->execute(); $provider_configs = mpull($provider_configs, null, 'getPHID'); foreach ($methods as $key => $method) { $provider_config = idx($provider_configs, $method->getProviderPHID()); if (!$provider_config) { unset($methods[$key]); continue; } $method->attachProviderConfig($provider_config); } return $methods; } - private function buildWhereClause(AphrontDatabaseConnection $conn) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->accountPHIDs !== null) { $where[] = qsprintf( $conn, 'accountPHID IN (%Ls)', $this->accountPHIDs); } if ($this->merchantPHIDs !== null) { $where[] = qsprintf( $conn, 'merchantPHID IN (%Ls)', $this->merchantPHIDs); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'status IN (%Ls)', $this->statuses); } $where[] = $this->buildPagingClause($conn); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorPhortuneApplication'; } } diff --git a/src/applications/phortune/query/PhortunePaymentProviderConfigQuery.php b/src/applications/phortune/query/PhortunePaymentProviderConfigQuery.php index 748ff7cb64..d80b0e90d0 100644 --- a/src/applications/phortune/query/PhortunePaymentProviderConfigQuery.php +++ b/src/applications/phortune/query/PhortunePaymentProviderConfigQuery.php @@ -1,95 +1,95 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withMerchantPHIDs(array $phids) { $this->merchantPHIDs = $phids; return $this; } protected function loadPage() { $table = new PhortunePaymentProviderConfig(); $conn = $table->establishConnection('r'); $rows = queryfx_all( $conn, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn), $this->buildOrderClause($conn), $this->buildLimitClause($conn)); return $table->loadAllFromArray($rows); } protected function willFilterPage(array $provider_configs) { $merchant_phids = mpull($provider_configs, 'getMerchantPHID'); $merchants = id(new PhortuneMerchantQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($merchant_phids) ->execute(); $merchants = mpull($merchants, null, 'getPHID'); foreach ($provider_configs as $key => $config) { $merchant = idx($merchants, $config->getMerchantPHID()); if (!$merchant) { $this->didRejectResult($config); unset($provider_configs[$key]); continue; } $config->attachMerchant($merchant); } return $provider_configs; } - private function buildWhereClause(AphrontDatabaseConnection $conn) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->merchantPHIDs !== null) { $where[] = qsprintf( $conn, 'merchantPHID IN (%Ls)', $this->merchantPHIDs); } $where[] = $this->buildPagingClause($conn); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorPhortuneApplication'; } } diff --git a/src/applications/phortune/query/PhortuneProductQuery.php b/src/applications/phortune/query/PhortuneProductQuery.php index ae824ab914..99ba535585 100644 --- a/src/applications/phortune/query/PhortuneProductQuery.php +++ b/src/applications/phortune/query/PhortuneProductQuery.php @@ -1,120 +1,120 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withClassAndRef($class, $ref) { $this->refMap = array($class => array($ref)); return $this; } protected function loadPage() { $table = new PhortuneProduct(); $conn = $table->establishConnection('r'); $rows = queryfx_all( $conn, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn), $this->buildOrderClause($conn), $this->buildLimitClause($conn)); $page = $table->loadAllFromArray($rows); // NOTE: We're loading product implementations here, but also creating any // products which do not yet exist. $class_map = mgroup($page, 'getProductClass'); if ($this->refMap) { $class_map += array_fill_keys(array_keys($this->refMap), array()); } foreach ($class_map as $class => $products) { $refs = mpull($products, null, 'getProductRef'); if (isset($this->refMap[$class])) { $refs += array_fill_keys($this->refMap[$class], null); } $implementations = newv($class, array())->loadImplementationsForRefs( $this->getViewer(), array_keys($refs)); $implementations = mpull($implementations, null, 'getRef'); foreach ($implementations as $ref => $implementation) { $product = idx($refs, $ref); if ($product === null) { // If this product does not exist yet, create it and add it to the // result page. $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); $product = PhortuneProduct::initializeNewProduct() ->setProductClass($class) ->setProductRef($ref) ->save(); unset($unguarded); $page[] = $product; } $product->attachImplementation($implementation); } } return $page; } - private function buildWhereClause(AphrontDatabaseConnection $conn) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->refMap !== null) { $sql = array(); foreach ($this->refMap as $class => $refs) { foreach ($refs as $ref) { $sql[] = qsprintf( $conn, '(productClassKey = %s AND productRefKey = %s)', PhabricatorHash::digestForIndex($class), PhabricatorHash::digestForIndex($ref)); } } $where[] = implode(' OR ', $sql); } $where[] = $this->buildPagingClause($conn); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorPhortuneApplication'; } } diff --git a/src/applications/phortune/query/PhortunePurchaseQuery.php b/src/applications/phortune/query/PhortunePurchaseQuery.php index 70aff445a0..6e9e599240 100644 --- a/src/applications/phortune/query/PhortunePurchaseQuery.php +++ b/src/applications/phortune/query/PhortunePurchaseQuery.php @@ -1,110 +1,110 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withCartPHIDs(array $cart_phids) { $this->cartPHIDs = $cart_phids; return $this; } protected function loadPage() { $table = new PhortunePurchase(); $conn = $table->establishConnection('r'); $rows = queryfx_all( $conn, 'SELECT purchase.* FROM %T purchase %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn), $this->buildOrderClause($conn), $this->buildLimitClause($conn)); return $table->loadAllFromArray($rows); } protected function willFilterPage(array $purchases) { $carts = id(new PhortuneCartQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs(mpull($purchases, 'getCartPHID')) ->execute(); $carts = mpull($carts, null, 'getPHID'); foreach ($purchases as $key => $purchase) { $cart = idx($carts, $purchase->getCartPHID()); if (!$cart) { unset($purchases[$key]); continue; } $purchase->attachCart($cart); } $products = id(new PhortuneProductQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs(mpull($purchases, 'getProductPHID')) ->execute(); $products = mpull($products, null, 'getPHID'); foreach ($purchases as $key => $purchase) { $product = idx($products, $purchase->getProductPHID()); if (!$product) { unset($purchases[$key]); continue; } $purchase->attachProduct($product); } return $purchases; } - private function buildWhereClause(AphrontDatabaseConnection $conn) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); $where[] = $this->buildPagingClause($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'purchase.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'purchase.phid IN (%Ls)', $this->phids); } if ($this->cartPHIDs !== null) { $where[] = qsprintf( $conn, 'purchase.cartPHID IN (%Ls)', $this->cartPHIDs); } return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorPhortuneApplication'; } } diff --git a/src/applications/phortune/query/PhortuneSubscriptionQuery.php b/src/applications/phortune/query/PhortuneSubscriptionQuery.php index ba6d7acc73..e8b39c0fee 100644 --- a/src/applications/phortune/query/PhortuneSubscriptionQuery.php +++ b/src/applications/phortune/query/PhortuneSubscriptionQuery.php @@ -1,192 +1,192 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAccountPHIDs(array $account_phids) { $this->accountPHIDs = $account_phids; return $this; } public function withMerchantPHIDs(array $merchant_phids) { $this->merchantPHIDs = $merchant_phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function needTriggers($need_triggers) { $this->needTriggers = $need_triggers; return $this; } protected function loadPage() { $table = new PhortuneSubscription(); $conn = $table->establishConnection('r'); $rows = queryfx_all( $conn, 'SELECT subscription.* FROM %T subscription %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn), $this->buildOrderClause($conn), $this->buildLimitClause($conn)); return $table->loadAllFromArray($rows); } protected function willFilterPage(array $subscriptions) { $accounts = id(new PhortuneAccountQuery()) ->setViewer($this->getViewer()) ->withPHIDs(mpull($subscriptions, 'getAccountPHID')) ->execute(); $accounts = mpull($accounts, null, 'getPHID'); foreach ($subscriptions as $key => $subscription) { $account = idx($accounts, $subscription->getAccountPHID()); if (!$account) { unset($subscriptions[$key]); continue; } $subscription->attachAccount($account); } if (!$subscriptions) { return $subscriptions; } $merchants = id(new PhortuneMerchantQuery()) ->setViewer($this->getViewer()) ->withPHIDs(mpull($subscriptions, 'getMerchantPHID')) ->execute(); $merchants = mpull($merchants, null, 'getPHID'); foreach ($subscriptions as $key => $subscription) { $merchant = idx($merchants, $subscription->getMerchantPHID()); if (!$merchant) { unset($subscriptions[$key]); continue; } $subscription->attachMerchant($merchant); } if (!$subscriptions) { return $subscriptions; } $implementations = array(); $subscription_map = mgroup($subscriptions, 'getSubscriptionClass'); foreach ($subscription_map as $class => $class_subscriptions) { $sub = newv($class, array()); $impl_objects = $sub->loadImplementationsForRefs( $this->getViewer(), mpull($class_subscriptions, 'getSubscriptionRef')); $implementations += mpull($impl_objects, null, 'getRef'); } foreach ($subscriptions as $key => $subscription) { $ref = $subscription->getSubscriptionRef(); $implementation = idx($implementations, $ref); if (!$implementation) { unset($subscriptions[$key]); continue; } $subscription->attachImplementation($implementation); } if (!$subscriptions) { return $subscriptions; } if ($this->needTriggers) { $trigger_phids = mpull($subscriptions, 'getTriggerPHID'); $triggers = id(new PhabricatorWorkerTriggerQuery()) ->setViewer($this->getViewer()) ->withPHIDs($trigger_phids) ->needEvents(true) ->execute(); $triggers = mpull($triggers, null, 'getPHID'); foreach ($subscriptions as $key => $subscription) { $trigger = idx($triggers, $subscription->getTriggerPHID()); if (!$trigger) { unset($subscriptions[$key]); continue; } $subscription->attachTrigger($trigger); } } return $subscriptions; } - private function buildWhereClause(AphrontDatabaseConnection $conn) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); $where[] = $this->buildPagingClause($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'subscription.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'subscription.phid IN (%Ls)', $this->phids); } if ($this->accountPHIDs !== null) { $where[] = qsprintf( $conn, 'subscription.accountPHID IN (%Ls)', $this->accountPHIDs); } if ($this->merchantPHIDs !== null) { $where[] = qsprintf( $conn, 'subscription.merchantPHID IN (%Ls)', $this->merchantPHIDs); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'subscription.status IN (%Ls)', $this->statuses); } return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorPhortuneApplication'; } } diff --git a/src/applications/phragment/query/PhragmentFragmentQuery.php b/src/applications/phragment/query/PhragmentFragmentQuery.php index 173acfb1ce..d6dc67609d 100644 --- a/src/applications/phragment/query/PhragmentFragmentQuery.php +++ b/src/applications/phragment/query/PhragmentFragmentQuery.php @@ -1,130 +1,130 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withPaths(array $paths) { $this->paths = $paths; return $this; } public function withLeadingPath($path) { $this->leadingPath = $path; return $this; } public function withDepths($depths) { $this->depths = $depths; return $this; } public function needLatestVersion($need_latest_version) { $this->needLatestVersion = $need_latest_version; return $this; } protected function loadPage() { $table = new PhragmentFragment(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->paths) { $where[] = qsprintf( $conn_r, 'path IN (%Ls)', $this->paths); } if ($this->leadingPath) { $where[] = qsprintf( $conn_r, 'path LIKE %>', $this->leadingPath); } if ($this->depths) { $where[] = qsprintf( $conn_r, 'depth IN (%Ld)', $this->depths); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } protected function didFilterPage(array $page) { if ($this->needLatestVersion) { $versions = array(); $version_phids = array_filter(mpull($page, 'getLatestVersionPHID')); if ($version_phids) { $versions = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($version_phids) ->setParentQuery($this) ->execute(); $versions = mpull($versions, null, 'getPHID'); } foreach ($page as $key => $fragment) { $version_phid = $fragment->getLatestVersionPHID(); if (empty($versions[$version_phid])) { continue; } $fragment->attachLatestVersion($versions[$version_phid]); } } return $page; } public function getQueryApplicationClass() { return 'PhabricatorPhragmentApplication'; } } diff --git a/src/applications/phragment/query/PhragmentFragmentVersionQuery.php b/src/applications/phragment/query/PhragmentFragmentVersionQuery.php index 4acbd0f636..a874a8be1a 100644 --- a/src/applications/phragment/query/PhragmentFragmentVersionQuery.php +++ b/src/applications/phragment/query/PhragmentFragmentVersionQuery.php @@ -1,123 +1,123 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withFragmentPHIDs(array $fragment_phids) { $this->fragmentPHIDs = $fragment_phids; return $this; } public function withSequences(array $sequences) { $this->sequences = $sequences; return $this; } public function withSequenceBefore($current) { $this->sequenceBefore = $current; return $this; } protected function loadPage() { $table = new PhragmentFragmentVersion(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->fragmentPHIDs) { $where[] = qsprintf( $conn_r, 'fragmentPHID IN (%Ls)', $this->fragmentPHIDs); } if ($this->sequences) { $where[] = qsprintf( $conn_r, 'sequence IN (%Ld)', $this->sequences); } if ($this->sequenceBefore !== null) { $where[] = qsprintf( $conn_r, 'sequence < %d', $this->sequenceBefore); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } protected function willFilterPage(array $page) { $fragments = array(); $fragment_phids = array_filter(mpull($page, 'getFragmentPHID')); if ($fragment_phids) { $fragments = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($fragment_phids) ->setParentQuery($this) ->execute(); $fragments = mpull($fragments, null, 'getPHID'); } foreach ($page as $key => $version) { $fragment_phid = $version->getFragmentPHID(); if (empty($fragments[$fragment_phid])) { unset($page[$key]); continue; } $version->attachFragment($fragments[$fragment_phid]); } return $page; } public function getQueryApplicationClass() { return 'PhabricatorPhragmentApplication'; } } diff --git a/src/applications/phragment/query/PhragmentSnapshotChildQuery.php b/src/applications/phragment/query/PhragmentSnapshotChildQuery.php index 8f81beeabe..032fb0c49c 100644 --- a/src/applications/phragment/query/PhragmentSnapshotChildQuery.php +++ b/src/applications/phragment/query/PhragmentSnapshotChildQuery.php @@ -1,174 +1,174 @@ ids = $ids; return $this; } public function withSnapshotPHIDs(array $snapshot_phids) { $this->snapshotPHIDs = $snapshot_phids; return $this; } public function withFragmentPHIDs(array $fragment_phids) { $this->fragmentPHIDs = $fragment_phids; return $this; } public function withFragmentVersionPHIDs(array $fragment_version_phids) { $this->fragmentVersionPHIDs = $fragment_version_phids; return $this; } public function needFragments($need_fragments) { $this->needFragments = $need_fragments; return $this; } public function needFragmentVersions($need_fragment_versions) { $this->needFragmentVersions = $need_fragment_versions; return $this; } protected function loadPage() { $table = new PhragmentSnapshotChild(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->snapshotPHIDs) { $where[] = qsprintf( $conn_r, 'snapshotPHID IN (%Ls)', $this->snapshotPHIDs); } if ($this->fragmentPHIDs) { $where[] = qsprintf( $conn_r, 'fragmentPHID IN (%Ls)', $this->fragmentPHIDs); } if ($this->fragmentVersionPHIDs) { $where[] = qsprintf( $conn_r, 'fragmentVersionPHID IN (%Ls)', $this->fragmentVersionPHIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } protected function willFilterPage(array $page) { $snapshots = array(); $snapshot_phids = array_filter(mpull($page, 'getSnapshotPHID')); if ($snapshot_phids) { $snapshots = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($snapshot_phids) ->setParentQuery($this) ->execute(); $snapshots = mpull($snapshots, null, 'getPHID'); } foreach ($page as $key => $child) { $snapshot_phid = $child->getSnapshotPHID(); if (empty($snapshots[$snapshot_phid])) { unset($page[$key]); continue; } $child->attachSnapshot($snapshots[$snapshot_phid]); } return $page; } protected function didFilterPage(array $page) { if ($this->needFragments) { $fragments = array(); $fragment_phids = array_filter(mpull($page, 'getFragmentPHID')); if ($fragment_phids) { $fragments = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($fragment_phids) ->setParentQuery($this) ->execute(); $fragments = mpull($fragments, null, 'getPHID'); } foreach ($page as $key => $child) { $fragment_phid = $child->getFragmentPHID(); if (empty($fragments[$fragment_phid])) { unset($page[$key]); continue; } $child->attachFragment($fragments[$fragment_phid]); } } if ($this->needFragmentVersions) { $fragment_versions = array(); $fragment_version_phids = array_filter(mpull( $page, 'getFragmentVersionPHID')); if ($fragment_version_phids) { $fragment_versions = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($fragment_version_phids) ->setParentQuery($this) ->execute(); $fragment_versions = mpull($fragment_versions, null, 'getPHID'); } foreach ($page as $key => $child) { $fragment_version_phid = $child->getFragmentVersionPHID(); if (empty($fragment_versions[$fragment_version_phid])) { continue; } $child->attachFragmentVersion( $fragment_versions[$fragment_version_phid]); } } return $page; } public function getQueryApplicationClass() { return 'PhabricatorPhragmentApplication'; } } diff --git a/src/applications/phragment/query/PhragmentSnapshotQuery.php b/src/applications/phragment/query/PhragmentSnapshotQuery.php index 81c73441d2..d6f9d3422f 100644 --- a/src/applications/phragment/query/PhragmentSnapshotQuery.php +++ b/src/applications/phragment/query/PhragmentSnapshotQuery.php @@ -1,111 +1,111 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withPrimaryFragmentPHIDs(array $primary_fragment_phids) { $this->primaryFragmentPHIDs = $primary_fragment_phids; return $this; } public function withNames(array $names) { $this->names = $names; return $this; } protected function loadPage() { $table = new PhragmentSnapshot(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - protected function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->primaryFragmentPHIDs) { $where[] = qsprintf( $conn_r, 'primaryFragmentPHID IN (%Ls)', $this->primaryFragmentPHIDs); } if ($this->names) { $where[] = qsprintf( $conn_r, 'name IN (%Ls)', $this->names); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } protected function willFilterPage(array $page) { $fragments = array(); $fragment_phids = array_filter(mpull($page, 'getPrimaryFragmentPHID')); if ($fragment_phids) { $fragments = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($fragment_phids) ->setParentQuery($this) ->execute(); $fragments = mpull($fragments, null, 'getPHID'); } foreach ($page as $key => $snapshot) { $fragment_phid = $snapshot->getPrimaryFragmentPHID(); if (empty($fragments[$fragment_phid])) { unset($page[$key]); continue; } $snapshot->attachPrimaryFragment($fragments[$fragment_phid]); } return $page; } public function getQueryApplicationClass() { return 'PhabricatorPhragmentApplication'; } } diff --git a/src/applications/phrequent/query/PhrequentUserTimeQuery.php b/src/applications/phrequent/query/PhrequentUserTimeQuery.php index b6e0c85f48..61fa978165 100644 --- a/src/applications/phrequent/query/PhrequentUserTimeQuery.php +++ b/src/applications/phrequent/query/PhrequentUserTimeQuery.php @@ -1,333 +1,333 @@ ids = $ids; return $this; } public function withUserPHIDs(array $user_phids) { $this->userPHIDs = $user_phids; return $this; } public function withObjectPHIDs(array $object_phids) { $this->objectPHIDs = $object_phids; return $this; } public function withEnded($ended) { $this->ended = $ended; return $this; } public function setOrder($order) { switch ($order) { case self::ORDER_ID_ASC: $this->setOrderVector(array('-id')); break; case self::ORDER_ID_DESC: $this->setOrderVector(array('id')); break; case self::ORDER_STARTED_ASC: $this->setOrderVector(array('-start', '-id')); break; case self::ORDER_STARTED_DESC: $this->setOrderVector(array('start', 'id')); break; case self::ORDER_ENDED_ASC: $this->setOrderVector(array('-end', '-id')); break; case self::ORDER_ENDED_DESC: $this->setOrderVector(array('end', 'id')); break; default: throw new Exception(pht('Unknown order "%s".', $order)); } return $this; } public function needPreemptingEvents($need_events) { $this->needPreemptingEvents = $need_events; return $this; } - private function buildWhereClause(AphrontDatabaseConnection $conn) { + protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->userPHIDs !== null) { $where[] = qsprintf( $conn, 'userPHID IN (%Ls)', $this->userPHIDs); } if ($this->objectPHIDs !== null) { $where[] = qsprintf( $conn, 'objectPHID IN (%Ls)', $this->objectPHIDs); } switch ($this->ended) { case self::ENDED_ALL: break; case self::ENDED_YES: $where[] = qsprintf( $conn, 'dateEnded IS NOT NULL'); break; case self::ENDED_NO: $where[] = qsprintf( $conn, 'dateEnded IS NULL'); break; default: throw new Exception("Unknown ended '{$this->ended}'!"); } $where[] = $this->buildPagingClause($conn); return $this->formatWhereClause($where); } public function getOrderableColumns() { return parent::getOrderableColumns() + array( 'start' => array( 'column' => 'dateStarted', 'type' => 'int', ), 'end' => array( 'column' => 'dateEnded', 'type' => 'int', 'null' => 'head', ), ); } protected function getPagingValueMap($cursor, array $keys) { $usertime = $this->loadCursorObject($cursor); return array( 'id' => $usertime->getID(), 'start' => $usertime->getDateStarted(), 'end' => $usertime->getDateEnded(), ); } protected function loadPage() { $usertime = new PhrequentUserTime(); $conn = $usertime->establishConnection('r'); $data = queryfx_all( $conn, 'SELECT usertime.* FROM %T usertime %Q %Q %Q', $usertime->getTableName(), $this->buildWhereClause($conn), $this->buildOrderClause($conn), $this->buildLimitClause($conn)); return $usertime->loadAllFromArray($data); } protected function didFilterPage(array $page) { if ($this->needPreemptingEvents) { $usertime = new PhrequentUserTime(); $conn_r = $usertime->establishConnection('r'); $preempt = array(); foreach ($page as $event) { $preempt[] = qsprintf( $conn_r, '(userPHID = %s AND (dateStarted BETWEEN %d AND %d) AND (dateEnded IS NULL OR dateEnded > %d))', $event->getUserPHID(), $event->getDateStarted(), nonempty($event->getDateEnded(), PhabricatorTime::getNow()), $event->getDateStarted()); } $preempting_events = queryfx_all( $conn_r, 'SELECT * FROM %T WHERE %Q ORDER BY dateStarted ASC, id ASC', $usertime->getTableName(), implode(' OR ', $preempt)); $preempting_events = $usertime->loadAllFromArray($preempting_events); $preempting_events = mgroup($preempting_events, 'getUserPHID'); foreach ($page as $event) { $e_start = $event->getDateStarted(); $e_end = $event->getDateEnded(); $select = array(); $user_events = idx($preempting_events, $event->getUserPHID(), array()); foreach ($user_events as $u_event) { if ($u_event->getID() == $event->getID()) { // Don't allow an event to preempt itself. continue; } $u_start = $u_event->getDateStarted(); $u_end = $u_event->getDateEnded(); if ($u_start < $e_start) { // This event started before our event started, so it's not // preempting us. continue; } if ($u_start == $e_start) { if ($u_event->getID() < $event->getID()) { // This event started at the same time as our event started, // but has a lower ID, so it's not preempting us. continue; } } if (($e_end !== null) && ($u_start > $e_end)) { // Our event has ended, and this event started after it ended. continue; } if (($u_end !== null) && ($u_end < $e_start)) { // This event ended before our event began. continue; } $select[] = $u_event; } $event->attachPreemptingEvents($select); } } return $page; } /* -( Helper Functions ) --------------------------------------------------- */ public static function getEndedSearchOptions() { return array( self::ENDED_ALL => pht('All'), self::ENDED_NO => pht('No'), self::ENDED_YES => pht('Yes'), ); } public static function getOrderSearchOptions() { return array( self::ORDER_STARTED_ASC => pht('by furthest start date'), self::ORDER_STARTED_DESC => pht('by nearest start date'), self::ORDER_ENDED_ASC => pht('by furthest end date'), self::ORDER_ENDED_DESC => pht('by nearest end date'), ); } public static function getUserTotalObjectsTracked( PhabricatorUser $user, $limit = PHP_INT_MAX) { $usertime_dao = new PhrequentUserTime(); $conn = $usertime_dao->establishConnection('r'); $count = queryfx_one( $conn, 'SELECT COUNT(usertime.id) N FROM %T usertime '. 'WHERE usertime.userPHID = %s '. 'AND usertime.dateEnded IS NULL '. 'LIMIT %d', $usertime_dao->getTableName(), $user->getPHID(), $limit); return $count['N']; } public static function isUserTrackingObject( PhabricatorUser $user, $phid) { $usertime_dao = new PhrequentUserTime(); $conn = $usertime_dao->establishConnection('r'); $count = queryfx_one( $conn, 'SELECT COUNT(usertime.id) N FROM %T usertime '. 'WHERE usertime.userPHID = %s '. 'AND usertime.objectPHID = %s '. 'AND usertime.dateEnded IS NULL', $usertime_dao->getTableName(), $user->getPHID(), $phid); return $count['N'] > 0; } public static function getUserTimeSpentOnObject( PhabricatorUser $user, $phid) { $usertime_dao = new PhrequentUserTime(); $conn = $usertime_dao->establishConnection('r'); // First calculate all the time spent where the // usertime blocks have ended. $sum_ended = queryfx_one( $conn, 'SELECT SUM(usertime.dateEnded - usertime.dateStarted) N '. 'FROM %T usertime '. 'WHERE usertime.userPHID = %s '. 'AND usertime.objectPHID = %s '. 'AND usertime.dateEnded IS NOT NULL', $usertime_dao->getTableName(), $user->getPHID(), $phid); // Now calculate the time spent where the usertime // blocks have not yet ended. $sum_not_ended = queryfx_one( $conn, 'SELECT SUM(UNIX_TIMESTAMP() - usertime.dateStarted) N '. 'FROM %T usertime '. 'WHERE usertime.userPHID = %s '. 'AND usertime.objectPHID = %s '. 'AND usertime.dateEnded IS NULL', $usertime_dao->getTableName(), $user->getPHID(), $phid); return $sum_ended['N'] + $sum_not_ended['N']; } public function getQueryApplicationClass() { return 'PhabricatorPhrequentApplication'; } } diff --git a/src/applications/ponder/query/PonderAnswerQuery.php b/src/applications/ponder/query/PonderAnswerQuery.php index 1b5c66a10c..0fe61bc9e1 100644 --- a/src/applications/ponder/query/PonderAnswerQuery.php +++ b/src/applications/ponder/query/PonderAnswerQuery.php @@ -1,125 +1,125 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs(array $phids) { $this->authorPHIDs = $phids; return $this; } public function withQuestionIDs(array $ids) { $this->questionIDs = $ids; return $this; } public function needViewerVotes($need_viewer_votes) { $this->needViewerVotes = $need_viewer_votes; return $this; } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs) { $where[] = qsprintf( $conn_r, 'authorPHID IN (%Ls)', $this->authorPHIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } protected function loadPage() { $answer = new PonderAnswer(); $conn_r = $answer->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT a.* FROM %T a %Q %Q %Q', $answer->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $answer->loadAllFromArray($data); } protected function willFilterPage(array $answers) { $questions = id(new PonderQuestionQuery()) ->setViewer($this->getViewer()) ->withIDs(mpull($answers, 'getQuestionID')) ->execute(); foreach ($answers as $key => $answer) { $question = idx($questions, $answer->getQuestionID()); if (!$question) { unset($answers[$key]); continue; } $answer->attachQuestion($question); } if ($this->needViewerVotes) { $viewer_phid = $this->getViewer()->getPHID(); $etype = PonderAnswerHasVotingUserEdgeType::EDGECONST; $edges = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(mpull($answers, 'getPHID')) ->withDestinationPHIDs(array($viewer_phid)) ->withEdgeTypes(array($etype)) ->needEdgeData(true) ->execute(); foreach ($answers as $answer) { $user_edge = idx( $edges[$answer->getPHID()][$etype], $viewer_phid, array()); $answer->attachUserVote($viewer_phid, idx($user_edge, 'data', 0)); } } return $answers; } public function getQueryApplicationClass() { return 'PhabricatorPonderApplication'; } } diff --git a/src/applications/ponder/query/PonderQuestionQuery.php b/src/applications/ponder/query/PonderQuestionQuery.php index 9cb8c81d40..51081b3203 100644 --- a/src/applications/ponder/query/PonderQuestionQuery.php +++ b/src/applications/ponder/query/PonderQuestionQuery.php @@ -1,182 +1,182 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs(array $phids) { $this->authorPHIDs = $phids; return $this; } public function withStatus($status) { $this->status = $status; return $this; } public function withAnswererPHIDs(array $phids) { $this->answererPHIDs = $phids; return $this; } public function needAnswers($need_answers) { $this->needAnswers = $need_answers; return $this; } public function needViewerVotes($need_viewer_votes) { $this->needViewerVotes = $need_viewer_votes; return $this; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'q.id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'q.phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs) { $where[] = qsprintf( $conn_r, 'q.authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->status) { switch ($this->status) { case self::STATUS_ANY: break; case self::STATUS_OPEN: $where[] = qsprintf( $conn_r, 'q.status = %d', PonderQuestionStatus::STATUS_OPEN); break; case self::STATUS_CLOSED: $where[] = qsprintf( $conn_r, 'q.status = %d', PonderQuestionStatus::STATUS_CLOSED); break; default: throw new Exception("Unknown status query '{$this->status}'!"); } } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } protected function loadPage() { $question = new PonderQuestion(); $conn_r = $question->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT q.* FROM %T q %Q %Q %Q %Q', $question->getTableName(), $this->buildJoinsClause($conn_r), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $question->loadAllFromArray($data); } protected function willFilterPage(array $questions) { if ($this->needAnswers) { $aquery = id(new PonderAnswerQuery()) ->setViewer($this->getViewer()) ->setOrderVector(array('-id')) ->withQuestionIDs(mpull($questions, 'getID')); if ($this->needViewerVotes) { $aquery->needViewerVotes($this->needViewerVotes); } $answers = $aquery->execute(); $answers = mgroup($answers, 'getQuestionID'); foreach ($questions as $question) { $question_answers = idx($answers, $question->getID(), array()); $question->attachAnswers(mpull($question_answers, null, 'getPHID')); } } if ($this->needViewerVotes) { $viewer_phid = $this->getViewer()->getPHID(); $etype = PonderQuestionHasVotingUserEdgeType::EDGECONST; $edges = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(mpull($questions, 'getPHID')) ->withDestinationPHIDs(array($viewer_phid)) ->withEdgeTypes(array($etype)) ->needEdgeData(true) ->execute(); foreach ($questions as $question) { $user_edge = idx( $edges[$question->getPHID()][$etype], $viewer_phid, array()); $question->attachUserVote($viewer_phid, idx($user_edge, 'data', 0)); } } return $questions; } private function buildJoinsClause(AphrontDatabaseConnection $conn_r) { $joins = array(); if ($this->answererPHIDs) { $answer_table = new PonderAnswer(); $joins[] = qsprintf( $conn_r, 'JOIN %T a ON a.questionID = q.id AND a.authorPHID IN (%Ls)', $answer_table->getTableName(), $this->answererPHIDs); } return implode(' ', $joins); } public function getQueryApplicationClass() { return 'PhabricatorPonderApplication'; } } diff --git a/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php b/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php index 87bd87ffd3..3348b4054d 100644 --- a/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php +++ b/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php @@ -1,306 +1,306 @@ ids = $ids; return $this; } public function withBoardPHIDs(array $board_phids) { $this->boardPHIDs = $board_phids; return $this; } public function withObjectPHIDs(array $object_phids) { $this->objectPHIDs = $object_phids; return $this; } /** * Find objects in specific columns. * * NOTE: Using this method activates logic which constructs virtual * column positions for objects not in any column, if you pass a default * column. Normally these results are not returned. * * @param list Columns to look for objects in. * @return this */ public function withColumns(array $columns) { assert_instances_of($columns, 'PhabricatorProjectColumn'); $this->columns = $columns; return $this; } public function needColumns($need_columns) { $this->needColumns = true; return $this; } /** * Skip implicit creation of column positions which are implied but do not * yet exist. * * This is primarily useful internally. * * @param bool True to skip implicit creation of column positions. * @return this */ public function setSkipImplicitCreate($skip) { $this->skipImplicitCreate = $skip; return $this; } // NOTE: For now, boards are always attached to projects. However, they might // not be in the future. This generalization just anticipates a future where // we let other types of objects (like users) have boards, or let boards // contain other types of objects. private function newPositionObject() { return new PhabricatorProjectColumnPosition(); } private function newColumnQuery() { return new PhabricatorProjectColumnQuery(); } private function getBoardMembershipEdgeTypes() { return array( PhabricatorProjectProjectHasObjectEdgeType::EDGECONST, ); } private function getBoardMembershipPHIDTypes() { return array( ManiphestTaskPHIDType::TYPECONST, ); } protected function loadPage() { $table = $this->newPositionObject(); $conn_r = $table->establishConnection('r'); // We're going to find results by combining two queries: one query finds // objects on a board column, while the other query finds objects not on // any board column and virtually puts them on the default column. $unions = array(); // First, find all the stuff that's actually on a column. $unions[] = qsprintf( $conn_r, 'SELECT * FROM %T %Q', $table->getTableName(), $this->buildWhereClause($conn_r)); // If we have a default column, find all the stuff that's not in any // column and put it in the default column. $must_type_filter = false; if ($this->columns && !$this->skipImplicitCreate) { $default_map = array(); foreach ($this->columns as $column) { if ($column->isDefaultColumn()) { $default_map[$column->getProjectPHID()] = $column->getPHID(); } } if ($default_map) { $where = array(); // Find the edges attached to the boards we have default columns for. $where[] = qsprintf( $conn_r, 'e.src IN (%Ls)', array_keys($default_map)); // Find only edges which describe a board relationship. $where[] = qsprintf( $conn_r, 'e.type IN (%Ld)', $this->getBoardMembershipEdgeTypes()); if ($this->boardPHIDs !== null) { // This should normally be redundant, but construct it anyway if // the caller has told us to. $where[] = qsprintf( $conn_r, 'e.src IN (%Ls)', $this->boardPHIDs); } if ($this->objectPHIDs !== null) { $where[] = qsprintf( $conn_r, 'e.dst IN (%Ls)', $this->objectPHIDs); } $where[] = qsprintf( $conn_r, 'p.id IS NULL'); $where = $this->formatWhereClause($where); $unions[] = qsprintf( $conn_r, 'SELECT NULL id, e.src boardPHID, NULL columnPHID, e.dst objectPHID, 0 sequence FROM %T e LEFT JOIN %T p ON e.src = p.boardPHID AND e.dst = p.objectPHID %Q', PhabricatorEdgeConfig::TABLE_NAME_EDGE, $table->getTableName(), $where); $must_type_filter = true; } } $data = queryfx_all( $conn_r, '%Q %Q %Q', implode(' UNION ALL ', $unions), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); // If we've picked up objects not in any column, we need to filter out any // matched objects which have the wrong edge type. if ($must_type_filter) { $allowed_types = array_fuse($this->getBoardMembershipPHIDTypes()); foreach ($data as $id => $row) { if ($row['columnPHID'] === null) { $object_phid = $row['objectPHID']; if (empty($allowed_types[phid_get_type($object_phid)])) { unset($data[$id]); } } } } $positions = $table->loadAllFromArray($data); // Find the implied positions which don't exist yet. If there are any, // we're going to create them. $create = array(); foreach ($positions as $position) { if ($position->getColumnPHID() === null) { $column_phid = idx($default_map, $position->getBoardPHID()); $position->setColumnPHID($column_phid); $create[] = $position; } } if ($create) { // If we're adding several objects to a column, insert the column // position objects in object ID order. This means that newly added // objects float to the top, and when a group of newly added objects // float up at the same time, the most recently created ones end up // highest in the list. $objects = id(new PhabricatorObjectQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withPHIDs(mpull($create, 'getObjectPHID')) ->execute(); $objects = mpull($objects, null, 'getPHID'); $objects = msort($objects, 'getID'); $create = mgroup($create, 'getObjectPHID'); $create = array_select_keys($create, array_keys($objects)) + $create; $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); foreach ($create as $object_phid => $create_positions) { foreach ($create_positions as $create_position) { $create_position->save(); } } unset($unguarded); } return $positions; } protected function willFilterPage(array $page) { if ($this->needColumns) { $column_phids = mpull($page, 'getColumnPHID'); $columns = $this->newColumnQuery() ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($column_phids) ->execute(); $columns = mpull($columns, null, 'getPHID'); foreach ($page as $key => $position) { $column = idx($columns, $position->getColumnPHID()); if (!$column) { unset($page[$key]); continue; } $position->attachColumn($column); } } return $page; } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->boardPHIDs !== null) { $where[] = qsprintf( $conn_r, 'boardPHID IN (%Ls)', $this->boardPHIDs); } if ($this->objectPHIDs !== null) { $where[] = qsprintf( $conn_r, 'objectPHID IN (%Ls)', $this->objectPHIDs); } if ($this->columns !== null) { $where[] = qsprintf( $conn_r, 'columnPHID IN (%Ls)', mpull($this->columns, 'getPHID')); } // NOTE: Explicitly not building the paging clause here, since it won't // work with the UNION. return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorProjectApplication'; } } diff --git a/src/applications/project/query/PhabricatorProjectColumnQuery.php b/src/applications/project/query/PhabricatorProjectColumnQuery.php index d369161fe3..c486968070 100644 --- a/src/applications/project/query/PhabricatorProjectColumnQuery.php +++ b/src/applications/project/query/PhabricatorProjectColumnQuery.php @@ -1,112 +1,112 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withProjectPHIDs(array $project_phids) { $this->projectPHIDs = $project_phids; return $this; } public function withStatuses(array $status) { $this->statuses = $status; return $this; } protected function loadPage() { $table = new PhabricatorProjectColumn(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $page) { $projects = array(); $project_phids = array_filter(mpull($page, 'getProjectPHID')); if ($project_phids) { $projects = id(new PhabricatorProjectQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($project_phids) ->execute(); $projects = mpull($projects, null, 'getPHID'); } foreach ($page as $key => $column) { $phid = $column->getProjectPHID(); $project = idx($projects, $phid); if (!$project) { unset($page[$key]); continue; } $column->attachProject($project); } return $page; } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->projectPHIDs) { $where[] = qsprintf( $conn_r, 'projectPHID IN (%Ls)', $this->projectPHIDs); } if ($this->statuses !== null) { $where[] = qsprintf( $conn_r, 'status IN (%Ld)', $this->statuses); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorProjectApplication'; } } diff --git a/src/applications/project/query/PhabricatorProjectQuery.php b/src/applications/project/query/PhabricatorProjectQuery.php index 4b6ad566a7..f743be7295 100644 --- a/src/applications/project/query/PhabricatorProjectQuery.php +++ b/src/applications/project/query/PhabricatorProjectQuery.php @@ -1,393 +1,393 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withStatus($status) { $this->status = $status; return $this; } public function withMemberPHIDs(array $member_phids) { $this->memberPHIDs = $member_phids; return $this; } public function withSlugs(array $slugs) { $this->slugs = $slugs; return $this; } public function withPhrictionSlugs(array $slugs) { $this->phrictionSlugs = $slugs; return $this; } public function withNames(array $names) { $this->names = $names; return $this; } public function withNameTokens(array $tokens) { $this->nameTokens = array_values($tokens); return $this; } public function withIcons(array $icons) { $this->icons = $icons; return $this; } public function withColors(array $colors) { $this->colors = $colors; return $this; } public function needMembers($need_members) { $this->needMembers = $need_members; return $this; } public function needWatchers($need_watchers) { $this->needWatchers = $need_watchers; return $this; } public function needImages($need_images) { $this->needImages = $need_images; return $this; } public function needSlugs($need_slugs) { $this->needSlugs = $need_slugs; return $this; } protected function getDefaultOrderVector() { return array('name'); } public function getOrderableColumns() { return array( 'name' => array( 'table' => $this->getPrimaryTableAlias(), 'column' => 'name', 'reverse' => true, 'type' => 'string', 'unique' => true, ), ); } protected function getPagingValueMap($cursor, array $keys) { $project = $this->loadCursorObject($cursor); return array( 'name' => $project->getName(), ); } protected function loadPage() { $table = new PhabricatorProject(); $conn_r = $table->establishConnection('r'); // NOTE: Because visibility checks for projects depend on whether or not // the user is a project member, we always load their membership. If we're // loading all members anyway we can piggyback on that; otherwise we // do an explicit join. $select_clause = ''; if (!$this->needMembers) { $select_clause = ', vm.dst viewerIsMember'; } $data = queryfx_all( $conn_r, 'SELECT p.* %Q FROM %T p %Q %Q %Q %Q %Q', $select_clause, $table->getTableName(), $this->buildJoinClause($conn_r), $this->buildWhereClause($conn_r), $this->buildGroupClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $projects = $table->loadAllFromArray($data); if ($projects) { $viewer_phid = $this->getViewer()->getPHID(); $project_phids = mpull($projects, 'getPHID'); $member_type = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST; $watcher_type = PhabricatorObjectHasWatcherEdgeType::EDGECONST; $need_edge_types = array(); if ($this->needMembers) { $need_edge_types[] = $member_type; } else { foreach ($data as $row) { $projects[$row['id']]->setIsUserMember( $viewer_phid, ($row['viewerIsMember'] !== null)); } } if ($this->needWatchers) { $need_edge_types[] = $watcher_type; } if ($need_edge_types) { $edges = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs($project_phids) ->withEdgeTypes($need_edge_types) ->execute(); if ($this->needMembers) { foreach ($projects as $project) { $phid = $project->getPHID(); $project->attachMemberPHIDs( array_keys($edges[$phid][$member_type])); $project->setIsUserMember( $viewer_phid, isset($edges[$phid][$member_type][$viewer_phid])); } } if ($this->needWatchers) { foreach ($projects as $project) { $phid = $project->getPHID(); $project->attachWatcherPHIDs( array_keys($edges[$phid][$watcher_type])); $project->setIsUserWatcher( $viewer_phid, isset($edges[$phid][$watcher_type][$viewer_phid])); } } } } return $projects; } protected function didFilterPage(array $projects) { if ($this->needImages) { $default = null; $file_phids = mpull($projects, 'getProfileImagePHID'); $files = id(new PhabricatorFileQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($file_phids) ->execute(); $files = mpull($files, null, 'getPHID'); foreach ($projects as $project) { $file = idx($files, $project->getProfileImagePHID()); if (!$file) { if (!$default) { $default = PhabricatorFile::loadBuiltin( $this->getViewer(), 'project.png'); } $file = $default; } $project->attachProfileImageFile($file); } } if ($this->needSlugs) { $slugs = id(new PhabricatorProjectSlug()) ->loadAllWhere( 'projectPHID IN (%Ls)', mpull($projects, 'getPHID')); $slugs = mgroup($slugs, 'getProjectPHID'); foreach ($projects as $project) { $project_slugs = idx($slugs, $project->getPHID(), array()); $project->attachSlugs($project_slugs); } } return $projects; } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->status != self::STATUS_ANY) { switch ($this->status) { case self::STATUS_OPEN: case self::STATUS_ACTIVE: $filter = array( PhabricatorProjectStatus::STATUS_ACTIVE, ); break; case self::STATUS_CLOSED: case self::STATUS_ARCHIVED: $filter = array( PhabricatorProjectStatus::STATUS_ARCHIVED, ); break; default: throw new Exception( "Unknown project status '{$this->status}'!"); } $where[] = qsprintf( $conn_r, 'status IN (%Ld)', $filter); } if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->memberPHIDs !== null) { $where[] = qsprintf( $conn_r, 'e.dst IN (%Ls)', $this->memberPHIDs); } if ($this->slugs !== null) { $where[] = qsprintf( $conn_r, 'slug.slug IN (%Ls)', $this->slugs); } if ($this->phrictionSlugs !== null) { $where[] = qsprintf( $conn_r, 'phrictionSlug IN (%Ls)', $this->phrictionSlugs); } if ($this->names !== null) { $where[] = qsprintf( $conn_r, 'name IN (%Ls)', $this->names); } if ($this->icons !== null) { $where[] = qsprintf( $conn_r, 'icon IN (%Ls)', $this->icons); } if ($this->colors !== null) { $where[] = qsprintf( $conn_r, 'color IN (%Ls)', $this->colors); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } private function buildGroupClause($conn_r) { if ($this->memberPHIDs || $this->nameTokens) { return 'GROUP BY p.id'; } else { return $this->buildApplicationSearchGroupClause($conn_r); } } private function buildJoinClause($conn_r) { $joins = array(); if (!$this->needMembers !== null) { $joins[] = qsprintf( $conn_r, 'LEFT JOIN %T vm ON vm.src = p.phid AND vm.type = %d AND vm.dst = %s', PhabricatorEdgeConfig::TABLE_NAME_EDGE, PhabricatorProjectProjectHasMemberEdgeType::EDGECONST, $this->getViewer()->getPHID()); } if ($this->memberPHIDs !== null) { $joins[] = qsprintf( $conn_r, 'JOIN %T e ON e.src = p.phid AND e.type = %d', PhabricatorEdgeConfig::TABLE_NAME_EDGE, PhabricatorProjectProjectHasMemberEdgeType::EDGECONST); } if ($this->slugs !== null) { $joins[] = qsprintf( $conn_r, 'JOIN %T slug on slug.projectPHID = p.phid', id(new PhabricatorProjectSlug())->getTableName()); } if ($this->nameTokens !== null) { foreach ($this->nameTokens as $key => $token) { $token_table = 'token_'.$key; $joins[] = qsprintf( $conn_r, 'JOIN %T %T ON %T.projectID = p.id AND %T.token LIKE %>', PhabricatorProject::TABLE_DATASOURCE_TOKEN, $token_table, $token_table, $token_table, $token); } } $joins[] = $this->buildApplicationSearchJoinClause($conn_r); return implode(' ', $joins); } public function getQueryApplicationClass() { return 'PhabricatorProjectApplication'; } protected function getPrimaryTableAlias() { return 'p'; } } diff --git a/src/applications/releeph/query/ReleephBranchQuery.php b/src/applications/releeph/query/ReleephBranchQuery.php index 43a600fa2d..201c2653d0 100644 --- a/src/applications/releeph/query/ReleephBranchQuery.php +++ b/src/applications/releeph/query/ReleephBranchQuery.php @@ -1,152 +1,152 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function needCutPointCommits($need_commits) { $this->needCutPointCommits = $need_commits; return $this; } public function withStatus($status) { $this->status = $status; return $this; } public function withProductPHIDs($product_phids) { $this->productPHIDs = $product_phids; return $this; } protected function loadPage() { $table = new ReleephBranch(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willExecute() { if ($this->productPHIDs !== null) { $products = id(new ReleephProductQuery()) ->setViewer($this->getViewer()) ->withPHIDs($this->productPHIDs) ->execute(); if (!$products) { throw new PhabricatorEmptyQueryException(); } $this->productIDs = mpull($products, 'getID'); } } protected function willFilterPage(array $branches) { $project_ids = mpull($branches, 'getReleephProjectID'); $projects = id(new ReleephProductQuery()) ->withIDs($project_ids) ->setViewer($this->getViewer()) ->execute(); foreach ($branches as $key => $branch) { $project_id = $project_ids[$key]; if (isset($projects[$project_id])) { $branch->attachProject($projects[$project_id]); } else { unset($branches[$key]); } } if ($this->needCutPointCommits) { $commit_phids = mpull($branches, 'getCutPointCommitPHID'); $commits = id(new DiffusionCommitQuery()) ->setViewer($this->getViewer()) ->withPHIDs($commit_phids) ->execute(); $commits = mpull($commits, null, 'getPHID'); foreach ($branches as $branch) { $commit = idx($commits, $branch->getCutPointCommitPHID()); $branch->attachCutPointCommit($commit); } } return $branches; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->productIDs !== null) { $where[] = qsprintf( $conn_r, 'releephProjectID IN (%Ld)', $this->productIDs); } $status = $this->status; switch ($status) { case self::STATUS_ALL: break; case self::STATUS_OPEN: $where[] = qsprintf( $conn_r, 'isActive = 1'); break; default: throw new Exception("Unknown status constant '{$status}'!"); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorReleephApplication'; } } diff --git a/src/applications/releeph/query/ReleephProductQuery.php b/src/applications/releeph/query/ReleephProductQuery.php index 2562004a06..6fe2b90db3 100644 --- a/src/applications/releeph/query/ReleephProductQuery.php +++ b/src/applications/releeph/query/ReleephProductQuery.php @@ -1,174 +1,174 @@ active = $active; return $this; } public function setOrder($order) { switch ($order) { case self::ORDER_ID: $this->setOrderVector(array('id')); break; case self::ORDER_NAME: $this->setOrderVector(array('name')); break; default: throw new Exception(pht('Order "%s" not supported.', $order)); } return $this; } public function withIDs(array $ids) { $this->ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withRepositoryPHIDs(array $repository_phids) { $this->repositoryPHIDs = $repository_phids; return $this; } public function needArcanistProjects($need) { $this->needArcanistProjects = $need; return $this; } protected function loadPage() { $table = new ReleephProject(); $conn_r = $table->establishConnection('r'); $rows = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($rows); } protected function willFilterPage(array $projects) { assert_instances_of($projects, 'ReleephProject'); $repository_phids = mpull($projects, 'getRepositoryPHID'); $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getViewer()) ->withPHIDs($repository_phids) ->execute(); $repositories = mpull($repositories, null, 'getPHID'); foreach ($projects as $key => $project) { $repo = idx($repositories, $project->getRepositoryPHID()); if (!$repo) { unset($projects[$key]); continue; } $project->attachRepository($repo); } return $projects; } protected function didFilterPage(array $products) { if ($this->needArcanistProjects) { $project_ids = array_filter(mpull($products, 'getArcanistProjectID')); if ($project_ids) { $projects = id(new PhabricatorRepositoryArcanistProject()) ->loadAllWhere('id IN (%Ld)', $project_ids); $projects = mpull($projects, null, 'getID'); } else { $projects = array(); } foreach ($products as $product) { $project_id = $product->getArcanistProjectID(); $project = idx($projects, $project_id); $product->attachArcanistProject($project); } } return $products; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->active !== null) { $where[] = qsprintf( $conn_r, 'isActive = %d', (int)$this->active); } if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ls)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->repositoryPHIDs !== null) { $where[] = qsprintf( $conn_r, 'repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getOrderableColumns() { return parent::getOrderableColumns() + array( 'name' => array( 'column' => 'name', 'unique' => true, 'reverse' => true, 'type' => 'string', ), ); } protected function getPagingValueMap($cursor, array $keys) { $product = $this->loadCursorObject($cursor); return array( 'id' => $product->getID(), 'name' => $product->getName(), ); } public function getQueryApplicationClass() { return 'PhabricatorReleephApplication'; } } diff --git a/src/applications/releeph/query/ReleephRequestQuery.php b/src/applications/releeph/query/ReleephRequestQuery.php index 84d35d288a..1a5965ad43 100644 --- a/src/applications/releeph/query/ReleephRequestQuery.php +++ b/src/applications/releeph/query/ReleephRequestQuery.php @@ -1,247 +1,247 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withBranchIDs(array $branch_ids) { $this->branchIDs = $branch_ids; return $this; } public function withStatus($status) { $this->status = $status; return $this; } public function withRequestedCommitPHIDs(array $requested_commit_phids) { $this->requestedCommitPHIDs = $requested_commit_phids; return $this; } public function withRequestorPHIDs(array $phids) { $this->requestorPHIDs = $phids; return $this; } public function withSeverities(array $severities) { $this->severities = $severities; return $this; } public function withRequestedObjectPHIDs(array $phids) { $this->requestedObjectPHIDs = $phids; return $this; } protected function loadPage() { $table = new ReleephRequest(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $requests) { // Load requested objects: you must be able to see an object to see // requests for it. $object_phids = mpull($requests, 'getRequestedObjectPHID'); $objects = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($object_phids) ->execute(); foreach ($requests as $key => $request) { $object_phid = $request->getRequestedObjectPHID(); $object = idx($objects, $object_phid); if (!$object) { unset($requests[$key]); continue; } $request->attachRequestedObject($object); } if ($this->severities) { $severities = array_fuse($this->severities); foreach ($requests as $key => $request) { // NOTE: Facebook uses a custom field here. if (ReleephDefaultFieldSelector::isFacebook()) { $severity = $request->getDetail('severity'); } else { $severity = $request->getDetail('releeph:severity'); } if (empty($severities[$severity])) { unset($requests[$key]); } } } $branch_ids = array_unique(mpull($requests, 'getBranchID')); $branches = id(new ReleephBranchQuery()) ->withIDs($branch_ids) ->setViewer($this->getViewer()) ->execute(); $branches = mpull($branches, null, 'getID'); foreach ($requests as $key => $request) { $branch = idx($branches, $request->getBranchID()); if (!$branch) { unset($requests[$key]); continue; } $request->attachBranch($branch); } // TODO: These should be serviced by the query, but are not currently // denormalized anywhere. For now, filter them here instead. Note that // we must perform this filtering *after* querying and attaching branches, // because request status depends on the product. $keep_status = array_fuse($this->getKeepStatusConstants()); if ($keep_status) { foreach ($requests as $key => $request) { if (empty($keep_status[$request->getStatus()])) { unset($requests[$key]); } } } return $requests; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->branchIDs !== null) { $where[] = qsprintf( $conn_r, 'branchID IN (%Ld)', $this->branchIDs); } if ($this->requestedCommitPHIDs !== null) { $where[] = qsprintf( $conn_r, 'requestCommitPHID IN (%Ls)', $this->requestedCommitPHIDs); } if ($this->requestorPHIDs !== null) { $where[] = qsprintf( $conn_r, 'requestUserPHID IN (%Ls)', $this->requestorPHIDs); } if ($this->requestedObjectPHIDs !== null) { $where[] = qsprintf( $conn_r, 'requestedObjectPHID IN (%Ls)', $this->requestedObjectPHIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } private function getKeepStatusConstants() { switch ($this->status) { case self::STATUS_ALL: return array(); case self::STATUS_OPEN: return array( ReleephRequestStatus::STATUS_REQUESTED, ReleephRequestStatus::STATUS_NEEDS_PICK, ReleephRequestStatus::STATUS_NEEDS_REVERT, ); case self::STATUS_REQUESTED: return array( ReleephRequestStatus::STATUS_REQUESTED, ); case self::STATUS_NEEDS_PULL: return array( ReleephRequestStatus::STATUS_NEEDS_PICK, ); case self::STATUS_REJECTED: return array( ReleephRequestStatus::STATUS_REJECTED, ); case self::STATUS_ABANDONED: return array( ReleephRequestStatus::STATUS_ABANDONED, ); case self::STATUS_PULLED: return array( ReleephRequestStatus::STATUS_PICKED, ); case self::STATUS_NEEDS_REVERT: return array( ReleephRequestStatus::NEEDS_REVERT, ); case self::STATUS_REVERTED: return array( ReleephRequestStatus::REVERTED, ); default: throw new Exception("Unknown status '{$this->status}'!"); } } public function getQueryApplicationClass() { return 'PhabricatorReleephApplication'; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryArcanistProjectQuery.php b/src/applications/repository/query/PhabricatorRepositoryArcanistProjectQuery.php index 5cbc730075..901f58cbdf 100644 --- a/src/applications/repository/query/PhabricatorRepositoryArcanistProjectQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryArcanistProjectQuery.php @@ -1,87 +1,87 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function needRepositories($need_repositories) { $this->needRepositories = $need_repositories; return $this; } protected function loadPage() { $table = new PhabricatorRepositoryArcanistProject(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $projects) { assert_instances_of($projects, 'PhabricatorRepositoryArcanistProject'); if ($this->needRepositories) { $repository_ids = mpull($projects, 'getRepositoryID'); $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getViewer()) ->withIDs($repository_ids) ->execute(); foreach ($projects as $project) { $repo = idx($repositories, $project->getRepositoryID()); $project->attachRepository($repo); } } return $projects; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { // TODO: Diffusion? Differential? return null; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryMirrorQuery.php b/src/applications/repository/query/PhabricatorRepositoryMirrorQuery.php index f6e7d22950..84c5ef74ce 100644 --- a/src/applications/repository/query/PhabricatorRepositoryMirrorQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryMirrorQuery.php @@ -1,99 +1,99 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withRepositoryPHIDs(array $repository_phids) { $this->repositoryPHIDs = $repository_phids; return $this; } protected function loadPage() { $table = new PhabricatorRepositoryMirror(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $mirrors) { assert_instances_of($mirrors, 'PhabricatorRepositoryMirror'); $repository_phids = mpull($mirrors, 'getRepositoryPHID'); if ($repository_phids) { $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getViewer()) ->withPHIDs($repository_phids) ->execute(); $repositories = mpull($repositories, null, 'getPHID'); } else { $repositories = array(); } foreach ($mirrors as $key => $mirror) { $phid = $mirror->getRepositoryPHID(); if (empty($repositories[$phid])) { unset($mirrors[$key]); continue; } $mirror->attachRepository($repositories[$phid]); } return $mirrors; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->repositoryPHIDs) { $where[] = qsprintf( $conn_r, 'repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorDiffusionApplication'; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php b/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php index 14b4e4d596..a3bed7abd3 100644 --- a/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php @@ -1,131 +1,131 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withRepositoryPHIDs(array $repository_phids) { $this->repositoryPHIDs = $repository_phids; return $this; } public function withPusherPHIDs(array $pusher_phids) { $this->pusherPHIDs = $pusher_phids; return $this; } public function needLogs($need_logs) { $this->needLogs = $need_logs; return $this; } protected function loadPage() { $table = new PhabricatorRepositoryPushEvent(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $events) { $repository_phids = mpull($events, 'getRepositoryPHID'); $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getViewer()) ->withPHIDs($repository_phids) ->execute(); $repositories = mpull($repositories, null, 'getPHID'); foreach ($events as $key => $event) { $phid = $event->getRepositoryPHID(); if (empty($repositories[$phid])) { unset($events[$key]); continue; } $event->attachRepository($repositories[$phid]); } return $events; } protected function didFilterPage(array $events) { $phids = mpull($events, 'getPHID'); if ($this->needLogs) { $logs = id(new PhabricatorRepositoryPushLogQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPushEventPHIDs($phids) ->execute(); $logs = mgroup($logs, 'getPushEventPHID'); foreach ($events as $key => $event) { $event_logs = idx($logs, $event->getPHID(), array()); $event->attachLogs($event_logs); } } return $events; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->repositoryPHIDs) { $where[] = qsprintf( $conn_r, 'repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } if ($this->pusherPHIDs) { $where[] = qsprintf( $conn_r, 'pusherPHID in (%Ls)', $this->pusherPHIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorDiffusionApplication'; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php b/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php index 7812f77abd..94a0b6922e 100644 --- a/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php @@ -1,146 +1,146 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withRepositoryPHIDs(array $repository_phids) { $this->repositoryPHIDs = $repository_phids; return $this; } public function withPusherPHIDs(array $pusher_phids) { $this->pusherPHIDs = $pusher_phids; return $this; } public function withRefTypes(array $ref_types) { $this->refTypes = $ref_types; return $this; } public function withNewRefs(array $new_refs) { $this->newRefs = $new_refs; return $this; } public function withPushEventPHIDs(array $phids) { $this->pushEventPHIDs = $phids; return $this; } protected function loadPage() { $table = new PhabricatorRepositoryPushLog(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $logs) { $event_phids = mpull($logs, 'getPushEventPHID'); $events = id(new PhabricatorObjectQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($event_phids) ->execute(); $events = mpull($events, null, 'getPHID'); foreach ($logs as $key => $log) { $event = idx($events, $log->getPushEventPHID()); if (!$event) { unset($logs[$key]); continue; } $log->attachPushEvent($event); } return $logs; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->repositoryPHIDs) { $where[] = qsprintf( $conn_r, 'repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } if ($this->pusherPHIDs) { $where[] = qsprintf( $conn_r, 'pusherPHID in (%Ls)', $this->pusherPHIDs); } if ($this->pushEventPHIDs) { $where[] = qsprintf( $conn_r, 'pushEventPHID in (%Ls)', $this->pushEventPHIDs); } if ($this->refTypes) { $where[] = qsprintf( $conn_r, 'refType IN (%Ls)', $this->refTypes); } if ($this->newRefs) { $where[] = qsprintf( $conn_r, 'refNew IN (%Ls)', $this->newRefs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorDiffusionApplication'; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryQuery.php b/src/applications/repository/query/PhabricatorRepositoryQuery.php index 1b4e90fb22..d9ea26433f 100644 --- a/src/applications/repository/query/PhabricatorRepositoryQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryQuery.php @@ -1,552 +1,552 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withCallsigns(array $callsigns) { $this->callsigns = $callsigns; return $this; } public function withIdentifiers(array $identifiers) { $ids = array(); $callsigns = array(); $phids = array(); foreach ($identifiers as $identifier) { if (ctype_digit($identifier)) { $ids[$identifier] = $identifier; } else { $repository_type = PhabricatorRepositoryRepositoryPHIDType::TYPECONST; if (phid_get_type($identifier) === $repository_type) { $phids[$identifier] = $identifier; } else { $callsigns[$identifier] = $identifier; } } } $this->numericIdentifiers = $ids; $this->callsignIdentifiers = $callsigns; $this->phidIdentifiers = $phids; return $this; } public function withStatus($status) { $this->status = $status; return $this; } public function withHosted($hosted) { $this->hosted = $hosted; return $this; } public function withTypes(array $types) { $this->types = $types; return $this; } public function withUUIDs(array $uuids) { $this->uuids = $uuids; return $this; } public function withNameContains($contains) { $this->nameContains = $contains; return $this; } public function withRemoteURIs(array $uris) { $this->remoteURIs = $uris; return $this; } public function withAnyProjects(array $projects) { $this->anyProjectPHIDs = $projects; return $this; } public function withDatasourceQuery($query) { $this->datasourceQuery = $query; return $this; } public function needCommitCounts($need_counts) { $this->needCommitCounts = $need_counts; return $this; } public function needMostRecentCommits($need_commits) { $this->needMostRecentCommits = $need_commits; return $this; } public function needProjectPHIDs($need_phids) { $this->needProjectPHIDs = $need_phids; return $this; } public function getBuiltinOrders() { return array( 'committed' => array( 'vector' => array('committed', 'id'), 'name' => pht('Most Recent Commit'), ), 'name' => array( 'vector' => array('name', 'id'), 'name' => pht('Name'), ), 'callsign' => array( 'vector' => array('callsign'), 'name' => pht('Callsign'), ), 'size' => array( 'vector' => array('size', 'id'), 'name' => pht('Size'), ), ) + parent::getBuiltinOrders(); } public function getIdentifierMap() { if ($this->identifierMap === null) { throw new Exception( 'You must execute() the query before accessing the identifier map.'); } return $this->identifierMap; } protected function willExecute() { $this->identifierMap = array(); } protected function loadPage() { $table = new PhabricatorRepository(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T r %Q %Q %Q %Q', $table->getTableName(), $this->buildJoinsClause($conn_r), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $repositories = $table->loadAllFromArray($data); if ($this->needCommitCounts) { $sizes = ipull($data, 'size', 'id'); foreach ($repositories as $id => $repository) { $repository->attachCommitCount(nonempty($sizes[$id], 0)); } } if ($this->needMostRecentCommits) { $commit_ids = ipull($data, 'lastCommitID', 'id'); $commit_ids = array_filter($commit_ids); if ($commit_ids) { $commits = id(new DiffusionCommitQuery()) ->setViewer($this->getViewer()) ->withIDs($commit_ids) ->execute(); } else { $commits = array(); } foreach ($repositories as $id => $repository) { $commit = null; if (idx($commit_ids, $id)) { $commit = idx($commits, $commit_ids[$id]); } $repository->attachMostRecentCommit($commit); } } return $repositories; } protected function willFilterPage(array $repositories) { assert_instances_of($repositories, 'PhabricatorRepository'); // TODO: Denormalize repository status into the PhabricatorRepository // table so we can do this filtering in the database. foreach ($repositories as $key => $repo) { $status = $this->status; switch ($status) { case self::STATUS_OPEN: if (!$repo->isTracked()) { unset($repositories[$key]); } break; case self::STATUS_CLOSED: if ($repo->isTracked()) { unset($repositories[$key]); } break; case self::STATUS_ALL: break; default: throw new Exception("Unknown status '{$status}'!"); } // TODO: This should also be denormalized. $hosted = $this->hosted; switch ($hosted) { case self::HOSTED_PHABRICATOR: if (!$repo->isHosted()) { unset($repositories[$key]); } break; case self::HOSTED_REMOTE: if ($repo->isHosted()) { unset($repositories[$key]); } break; case self::HOSTED_ALL: break; default: throw new Exception("Uknown hosted failed '${hosted}'!"); } } // TODO: Denormalize this, too. if ($this->remoteURIs) { $try_uris = $this->getNormalizedPaths(); $try_uris = array_fuse($try_uris); foreach ($repositories as $key => $repository) { if (!isset($try_uris[$repository->getNormalizedPath()])) { unset($repositories[$key]); } } } // Build the identifierMap if ($this->numericIdentifiers) { foreach ($this->numericIdentifiers as $id) { if (isset($repositories[$id])) { $this->identifierMap[$id] = $repositories[$id]; } } } if ($this->callsignIdentifiers) { $repository_callsigns = mpull($repositories, null, 'getCallsign'); foreach ($this->callsignIdentifiers as $callsign) { if (isset($repository_callsigns[$callsign])) { $this->identifierMap[$callsign] = $repository_callsigns[$callsign]; } } } if ($this->phidIdentifiers) { $repository_phids = mpull($repositories, null, 'getPHID'); foreach ($this->phidIdentifiers as $phid) { if (isset($repository_phids[$phid])) { $this->identifierMap[$phid] = $repository_phids[$phid]; } } } return $repositories; } protected function didFilterPage(array $repositories) { if ($this->needProjectPHIDs) { $type_project = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; $edge_query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(mpull($repositories, 'getPHID')) ->withEdgeTypes(array($type_project)); $edge_query->execute(); foreach ($repositories as $repository) { $project_phids = $edge_query->getDestinationPHIDs( array( $repository->getPHID(), )); $repository->attachProjectPHIDs($project_phids); } } return $repositories; } protected function getPrimaryTableAlias() { return 'r'; } public function getOrderableColumns() { return parent::getOrderableColumns() + array( 'committed' => array( 'table' => 's', 'column' => 'epoch', 'type' => 'int', 'null' => 'tail', ), 'callsign' => array( 'table' => 'r', 'column' => 'callsign', 'type' => 'string', 'unique' => true, 'reverse' => true, ), 'name' => array( 'table' => 'r', 'column' => 'name', 'type' => 'string', 'reverse' => true, ), 'size' => array( 'table' => 's', 'column' => 'size', 'type' => 'int', 'null' => 'tail', ), ); } protected function willExecuteCursorQuery( PhabricatorCursorPagedPolicyAwareQuery $query) { $vector = $this->getOrderVector(); if ($vector->containsKey('committed')) { $query->needMostRecentCommits(true); } if ($vector->containsKey('size')) { $query->needCommitCounts(true); } } protected function getPagingValueMap($cursor, array $keys) { $repository = $this->loadCursorObject($cursor); $map = array( 'id' => $repository->getID(), 'callsign' => $repository->getCallsign(), 'name' => $repository->getName(), ); foreach ($keys as $key) { switch ($key) { case 'committed': $commit = $repository->getMostRecentCommit(); if ($commit) { $map[$key] = $commit->getEpoch(); } else { $map[$key] = null; } break; case 'size': $count = $repository->getCommitCount(); if ($count) { $map[$key] = $count; } else { $map[$key] = null; } break; } } return $map; } private function buildJoinsClause(AphrontDatabaseConnection $conn_r) { $joins = array(); $join_summary_table = $this->needCommitCounts || $this->needMostRecentCommits; $vector = $this->getOrderVector(); if ($vector->containsKey('committed') || $vector->containsKey('size')) { $join_summary_table = true; } if ($join_summary_table) { $joins[] = qsprintf( $conn_r, 'LEFT JOIN %T s ON r.id = s.repositoryID', PhabricatorRepository::TABLE_SUMMARY); } if ($this->anyProjectPHIDs) { $joins[] = qsprintf( $conn_r, 'JOIN edge e ON e.src = r.phid'); } return implode(' ', $joins); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'r.id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'r.phid IN (%Ls)', $this->phids); } if ($this->callsigns) { $where[] = qsprintf( $conn_r, 'r.callsign IN (%Ls)', $this->callsigns); } if ($this->numericIdentifiers || $this->callsignIdentifiers || $this->phidIdentifiers) { $identifier_clause = array(); if ($this->numericIdentifiers) { $identifier_clause[] = qsprintf( $conn_r, 'r.id IN (%Ld)', $this->numericIdentifiers); } if ($this->callsignIdentifiers) { $identifier_clause[] = qsprintf( $conn_r, 'r.callsign IN (%Ls)', $this->callsignIdentifiers); } if ($this->phidIdentifiers) { $identifier_clause[] = qsprintf( $conn_r, 'r.phid IN (%Ls)', $this->phidIdentifiers); } $where = array('('.implode(' OR ', $identifier_clause).')'); } if ($this->types) { $where[] = qsprintf( $conn_r, 'r.versionControlSystem IN (%Ls)', $this->types); } if ($this->uuids) { $where[] = qsprintf( $conn_r, 'r.uuid IN (%Ls)', $this->uuids); } if (strlen($this->nameContains)) { $where[] = qsprintf( $conn_r, 'name LIKE %~', $this->nameContains); } if ($this->anyProjectPHIDs) { $where[] = qsprintf( $conn_r, 'e.dst IN (%Ls)', $this->anyProjectPHIDs); } if (strlen($this->datasourceQuery)) { // This handles having "rP" match callsigns starting with "P...". $query = trim($this->datasourceQuery); if (preg_match('/^r/', $query)) { $callsign = substr($query, 1); } else { $callsign = $query; } $where[] = qsprintf( $conn_r, 'r.name LIKE %> OR r.callsign LIKE %>', $query, $callsign); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorDiffusionApplication'; } private function getNormalizedPaths() { $normalized_uris = array(); // Since we don't know which type of repository this URI is in the general // case, just generate all the normalizations. We could refine this in some // cases: if the query specifies VCS types, or the URI is a git-style URI // or an `svn+ssh` URI, we could deduce how to normalize it. However, this // would be more complicated and it's not clear if it matters in practice. foreach ($this->remoteURIs as $uri) { $normalized_uris[] = new PhabricatorRepositoryURINormalizer( PhabricatorRepositoryURINormalizer::TYPE_GIT, $uri); $normalized_uris[] = new PhabricatorRepositoryURINormalizer( PhabricatorRepositoryURINormalizer::TYPE_SVN, $uri); $normalized_uris[] = new PhabricatorRepositoryURINormalizer( PhabricatorRepositoryURINormalizer::TYPE_MERCURIAL, $uri); } return array_unique(mpull($normalized_uris, 'getNormalizedPath')); } } diff --git a/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php b/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php index b75a8aa31e..c4c48c713c 100644 --- a/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php @@ -1,100 +1,100 @@ repositoryPHIDs = $phids; return $this; } public function withRefTypes(array $types) { $this->refTypes = $types; return $this; } public function withRefNames(array $names) { $this->refNames = $names; return $this; } protected function loadPage() { $table = new PhabricatorRepositoryRefCursor(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T r %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $refs) { $repository_phids = mpull($refs, 'getRepositoryPHID'); $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($repository_phids) ->execute(); $repositories = mpull($repositories, null, 'getPHID'); foreach ($refs as $key => $ref) { $repository = idx($repositories, $ref->getRepositoryPHID()); if (!$repository) { unset($refs[$key]); continue; } $ref->attachRepository($repository); } return $refs; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->repositoryPHIDs !== null) { $where[] = qsprintf( $conn_r, 'repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } if ($this->refTypes !== null) { $where[] = qsprintf( $conn_r, 'refType IN (%Ls)', $this->refTypes); } if ($this->refNames !== null) { $name_hashes = array(); foreach ($this->refNames as $name) { $name_hashes[] = PhabricatorHash::digestForIndex($name); } $where[] = qsprintf( $conn_r, 'refNameHash IN (%Ls)', $name_hashes); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorDiffusionApplication'; } } diff --git a/src/applications/search/query/PhabricatorNamedQueryQuery.php b/src/applications/search/query/PhabricatorNamedQueryQuery.php index 413811771d..ce54ae9a84 100644 --- a/src/applications/search/query/PhabricatorNamedQueryQuery.php +++ b/src/applications/search/query/PhabricatorNamedQueryQuery.php @@ -1,86 +1,86 @@ ids = $ids; return $this; } public function withUserPHIDs(array $user_phids) { $this->userPHIDs = $user_phids; return $this; } public function withEngineClassNames(array $engine_class_names) { $this->engineClassNames = $engine_class_names; return $this; } public function withQueryKeys(array $query_keys) { $this->queryKeys = $query_keys; return $this; } protected function loadPage() { $table = new PhabricatorNamedQuery(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->engineClassNames) { $where[] = qsprintf( $conn_r, 'engineClassName IN (%Ls)', $this->engineClassNames); } if ($this->userPHIDs) { $where[] = qsprintf( $conn_r, 'userPHID IN (%Ls)', $this->userPHIDs); } if ($this->queryKeys) { $where[] = qsprintf( $conn_r, 'queryKey IN (%Ls)', $this->queryKeys); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorSearchApplication'; } } diff --git a/src/applications/search/query/PhabricatorSavedQueryQuery.php b/src/applications/search/query/PhabricatorSavedQueryQuery.php index 168342fdf2..623b001662 100644 --- a/src/applications/search/query/PhabricatorSavedQueryQuery.php +++ b/src/applications/search/query/PhabricatorSavedQueryQuery.php @@ -1,73 +1,73 @@ ids = $ids; return $this; } public function withEngineClassNames(array $engine_class_names) { $this->engineClassNames = $engine_class_names; return $this; } public function withQueryKeys(array $query_keys) { $this->queryKeys = $query_keys; return $this; } protected function loadPage() { $table = new PhabricatorSavedQuery(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->engineClassNames !== null) { $where[] = qsprintf( $conn_r, 'engineClassName IN (%Ls)', $this->engineClassNames); } if ($this->queryKeys !== null) { $where[] = qsprintf( $conn_r, 'queryKey IN (%Ls)', $this->queryKeys); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { return 'PhabricatorSearchApplication'; } } diff --git a/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php b/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php index d17672b5e3..b7449d06a9 100644 --- a/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php +++ b/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php @@ -1,185 +1,185 @@ ids = $ids; return $this; } public function withPHIDs($phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs($author_phids) { $this->authorPHIDs = $author_phids; return $this; } public function withVotesByViewer($with_vote) { $this->withVotesByViewer = $with_vote; return $this; } public function withIsClosed($with_closed) { $this->isClosed = $with_closed; return $this; } public function needOptions($need_options) { $this->needOptions = $need_options; return $this; } public function needChoices($need_choices) { $this->needChoices = $need_choices; return $this; } public function needViewerChoices($need_viewer_choices) { $this->needViewerChoices = $need_viewer_choices; return $this; } protected function loadPage() { $table = new PhabricatorSlowvotePoll(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT p.* FROM %T p %Q %Q %Q %Q', $table->getTableName(), $this->buildJoinsClause($conn_r), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } protected function willFilterPage(array $polls) { assert_instances_of($polls, 'PhabricatorSlowvotePoll'); $ids = mpull($polls, 'getID'); $viewer = $this->getViewer(); if ($this->needOptions) { $options = id(new PhabricatorSlowvoteOption())->loadAllWhere( 'pollID IN (%Ld)', $ids); $options = mgroup($options, 'getPollID'); foreach ($polls as $poll) { $poll->attachOptions(idx($options, $poll->getID(), array())); } } if ($this->needChoices) { $choices = id(new PhabricatorSlowvoteChoice())->loadAllWhere( 'pollID IN (%Ld)', $ids); $choices = mgroup($choices, 'getPollID'); foreach ($polls as $poll) { $poll->attachChoices(idx($choices, $poll->getID(), array())); } // If we need the viewer's choices, we can just fill them from the data // we already loaded. if ($this->needViewerChoices) { foreach ($polls as $poll) { $poll->attachViewerChoices( $viewer, idx( mgroup($poll->getChoices(), 'getAuthorPHID'), $viewer->getPHID(), array())); } } } else if ($this->needViewerChoices) { $choices = id(new PhabricatorSlowvoteChoice())->loadAllWhere( 'pollID IN (%Ld) AND authorPHID = %s', $ids, $viewer->getPHID()); $choices = mgroup($choices, 'getPollID'); foreach ($polls as $poll) { $poll->attachViewerChoices( $viewer, idx($choices, $poll->getID(), array())); } } return $polls; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'p.id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'p.phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs) { $where[] = qsprintf( $conn_r, 'p.authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->isClosed !== null) { $where[] = qsprintf( $conn_r, 'p.isClosed = %d', (int)$this->isClosed); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } private function buildJoinsClause(AphrontDatabaseConnection $conn_r) { $joins = array(); if ($this->withVotesByViewer) { $joins[] = qsprintf( $conn_r, 'JOIN %T vv ON vv.pollID = p.id AND vv.authorPHID = %s', id(new PhabricatorSlowvoteChoice())->getTableName(), $this->getViewer()->getPHID()); } return implode(' ', $joins); } protected function getPrimaryTableAlias() { return 'p'; } public function getQueryApplicationClass() { return 'PhabricatorSlowvoteApplication'; } } diff --git a/src/applications/tokens/query/PhabricatorTokenCountQuery.php b/src/applications/tokens/query/PhabricatorTokenCountQuery.php index cc333109a2..d942f22ce4 100644 --- a/src/applications/tokens/query/PhabricatorTokenCountQuery.php +++ b/src/applications/tokens/query/PhabricatorTokenCountQuery.php @@ -1,40 +1,40 @@ objectPHIDs = $object_phids; return $this; } final public function execute() { $table = new PhabricatorTokenCount(); $conn_r = $table->establishConnection('r'); $rows = queryfx_all( $conn_r, 'SELECT objectPHID, tokenCount FROM %T %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildLimitClause($conn_r)); return ipull($rows, 'tokenCount', 'objectPHID'); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->objectPHIDs) { $where[] = qsprintf( $conn_r, 'objectPHID IN (%Ls)', $this->objectPHIDs); } return $this->formatWhereClause($where); } } diff --git a/src/applications/tokens/query/PhabricatorTokenGivenQuery.php b/src/applications/tokens/query/PhabricatorTokenGivenQuery.php index 896b4532c9..c16eaa2168 100644 --- a/src/applications/tokens/query/PhabricatorTokenGivenQuery.php +++ b/src/applications/tokens/query/PhabricatorTokenGivenQuery.php @@ -1,96 +1,96 @@ tokenPHIDs = $token_phids; return $this; } public function withObjectPHIDs(array $object_phids) { $this->objectPHIDs = $object_phids; return $this; } public function withAuthorPHIDs(array $author_phids) { $this->authorPHIDs = $author_phids; return $this; } protected function loadPage() { $table = new PhabricatorTokenGiven(); $conn_r = $table->establishConnection('r'); $rows = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($rows); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->authorPHIDs) { $where[] = qsprintf( $conn_r, 'authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->objectPHIDs) { $where[] = qsprintf( $conn_r, 'objectPHID IN (%Ls)', $this->objectPHIDs); } if ($this->tokenPHIDs) { $where[] = qsprintf( $conn_r, 'tokenPHID IN (%Ls)', $this->tokenPHIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } protected function willFilterPage(array $results) { $object_phids = array_filter(mpull($results, 'getObjectPHID')); if (!$object_phids) { return array(); } $objects = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($object_phids) ->execute(); foreach ($results as $key => $result) { $phid = $result->getObjectPHID(); if (empty($objects[$phid])) { unset($results[$key]); } else { $result->attachObject($objects[$phid]); } } return $results; } public function getQueryApplicationClass() { return 'PhabricatorTokensApplication'; } } diff --git a/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php b/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php index cd61c0fd30..f79de81ba3 100644 --- a/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php +++ b/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php @@ -1,125 +1,125 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withTransactionPHIDs(array $transaction_phids) { $this->transactionPHIDs = $transaction_phids; return $this; } public function withAuthorPHIDs(array $phids) { $this->authorPHIDs = $phids; return $this; } public function withIsDeleted($deleted) { $this->isDeleted = $deleted; return $this; } public function withHasTransaction($has_transaction) { $this->hasTransaction = $has_transaction; return $this; } protected function loadPage() { $table = $this->getTemplate(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T xcomment %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { return $this->formatWhereClause($this->buildWhereClauseComponents($conn_r)); } protected function buildWhereClauseComponents( AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'xcomment.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 'xcomment.phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn_r, 'xcomment.authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->transactionPHIDs !== null) { $where[] = qsprintf( $conn_r, 'xcomment.transactionPHID IN (%Ls)', $this->transactionPHIDs); } if ($this->isDeleted !== null) { $where[] = qsprintf( $conn_r, 'xcomment.isDeleted = %d', (int)$this->isDeleted); } if ($this->hasTransaction !== null) { if ($this->hasTransaction) { $where[] = qsprintf( $conn_r, 'xcomment.transactionPHID IS NOT NULL'); } else { $where[] = qsprintf( $conn_r, 'xcomment.transactionPHID IS NULL'); } } return $where; } public function getQueryApplicationClass() { // TODO: Figure out the app via the template? return null; } } diff --git a/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php b/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php index 5da8411277..ac6f713eeb 100644 --- a/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php +++ b/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php @@ -1,191 +1,191 @@ phids = $phids; return $this; } public function withObjectPHIDs(array $object_phids) { $this->objectPHIDs = $object_phids; return $this; } public function withAuthorPHIDs(array $author_phids) { $this->authorPHIDs = $author_phids; return $this; } public function withTransactionTypes(array $transaction_types) { $this->transactionTypes = $transaction_types; return $this; } public function needComments($need) { $this->needComments = $need; return $this; } public function needHandles($need) { $this->needHandles = $need; return $this; } protected function loadPage() { $table = $this->getTemplateApplicationTransaction(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T x %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $xactions = $table->loadAllFromArray($data); foreach ($xactions as $xaction) { $xaction->attachViewer($this->getViewer()); } if ($this->needComments) { $comment_phids = array_filter(mpull($xactions, 'getCommentPHID')); $comments = array(); if ($comment_phids) { $comments = id(new PhabricatorApplicationTransactionTemplatedCommentQuery()) ->setTemplate($table->getApplicationTransactionCommentObject()) ->setViewer($this->getViewer()) ->withPHIDs($comment_phids) ->execute(); $comments = mpull($comments, null, 'getPHID'); } foreach ($xactions as $xaction) { if ($xaction->getCommentPHID()) { $comment = idx($comments, $xaction->getCommentPHID()); if ($comment) { $xaction->attachComment($comment); } } } } else { foreach ($xactions as $xaction) { $xaction->setCommentNotLoaded(true); } } return $xactions; } protected function willFilterPage(array $xactions) { $object_phids = array_keys(mpull($xactions, null, 'getObjectPHID')); $objects = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($object_phids) ->execute(); foreach ($xactions as $key => $xaction) { $object_phid = $xaction->getObjectPHID(); if (empty($objects[$object_phid])) { unset($xactions[$key]); continue; } $xaction->attachObject($objects[$object_phid]); } // NOTE: We have to do this after loading objects, because the objects // may help determine which handles are required (for example, in the case // of custom fields). if ($this->needHandles) { $phids = array(); foreach ($xactions as $xaction) { $phids[$xaction->getPHID()] = $xaction->getRequiredHandlePHIDs(); } $handles = array(); $merged = array_mergev($phids); if ($merged) { $handles = id(new PhabricatorHandleQuery()) ->setViewer($this->getViewer()) ->withPHIDs($merged) ->execute(); } foreach ($xactions as $xaction) { $xaction->setHandles( array_select_keys( $handles, $phids[$xaction->getPHID()])); } } return $xactions; } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->objectPHIDs) { $where[] = qsprintf( $conn_r, 'objectPHID IN (%Ls)', $this->objectPHIDs); } if ($this->authorPHIDs) { $where[] = qsprintf( $conn_r, 'authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->transactionTypes) { $where[] = qsprintf( $conn_r, 'transactionType IN (%Ls)', $this->transactionTypes); } foreach ($this->buildMoreWhereClauses($conn_r) as $clause) { $where[] = $clause; } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } public function getQueryApplicationClass() { // TODO: Sort this out? return null; } } diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerArchiveTaskQuery.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerArchiveTaskQuery.php index eb46857a4f..7dfafa16cc 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerArchiveTaskQuery.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerArchiveTaskQuery.php @@ -1,94 +1,94 @@ ids = $ids; return $this; } public function withDateModifiedSince($timestamp) { $this->dateModifiedSince = $timestamp; return $this; } public function withDateCreatedBefore($timestamp) { $this->dateCreatedBefore = $timestamp; return $this; } public function withObjectPHIDs(array $phids) { $this->objectPHIDs = $phids; return $this; } public function setLimit($limit) { $this->limit = $limit; return $this; } public function execute() { $task_table = new PhabricatorWorkerArchiveTask(); $conn_r = $task_table->establishConnection('r'); $rows = queryfx_all( $conn_r, 'SELECT * FROM %T %Q ORDER BY id DESC %Q', $task_table->getTableName(), $this->buildWhereClause($conn_r), $this->buildLimitClause($conn_r)); return $task_table->loadAllFromArray($rows); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 'id in (%Ld)', $this->ids); } if ($this->objectPHIDs !== null) { $where[] = qsprintf( $conn_r, 'objectPHID IN (%Ls)', $this->objectPHIDs); } if ($this->dateModifiedSince) { $where[] = qsprintf( $conn_r, 'dateModified > %d', $this->dateModifiedSince); } if ($this->dateCreatedBefore) { $where[] = qsprintf( $conn_r, 'dateCreated < %d', $this->dateCreatedBefore); } return $this->formatWhereClause($where); } private function buildLimitClause(AphrontDatabaseConnection $conn_r) { $clause = ''; if ($this->limit) { $clause = qsprintf($conn_r, 'LIMIT %d', $this->limit); } return $clause; } } diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerLeaseQuery.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerLeaseQuery.php index 456a042776..07c3460494 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerLeaseQuery.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerLeaseQuery.php @@ -1,332 +1,335 @@ skipLease = $skip; return $this; } public function withIDs(array $ids) { $this->ids = $ids; return $this; } public function withObjectPHIDs(array $phids) { $this->objectPHIDs = $phids; return $this; } /** * Select only leased tasks, only unleased tasks, or both types of task. * * By default, queries select only unleased tasks (equivalent to passing * `false` to this method). You can pass `true` to select only leased tasks, * or `null` to ignore the lease status of tasks. * * If your result set potentially includes leased tasks, you must disable * leasing using @{method:setSkipLease}. These options are intended for use * when displaying task status information. * * @param mixed `true` to select only leased tasks, `false` to select only * unleased tasks (default), or `null` to select both. * @return this */ public function withLeasedTasks($leased) { $this->leased = $leased; return $this; } public function setLimit($limit) { $this->limit = $limit; return $this; } public function execute() { if (!$this->limit) { throw new Exception( pht('You must setLimit() when leasing tasks.')); } if ($this->leased !== false) { if (!$this->skipLease) { throw new Exception( pht( 'If you potentially select leased tasks using withLeasedTasks(), '. 'you MUST disable lease acquisition by calling setSkipLease().')); } } $task_table = new PhabricatorWorkerActiveTask(); $taskdata_table = new PhabricatorWorkerTaskData(); $lease_ownership_name = $this->getLeaseOwnershipName(); $conn_w = $task_table->establishConnection('w'); // Try to satisfy the request from new, unleased tasks first. If we don't // find enough tasks, try tasks with expired leases (i.e., tasks which have // previously failed). // If we're selecting leased tasks, look for them first. $phases = array(); if ($this->leased !== false) { $phases[] = self::PHASE_LEASED; } if ($this->leased !== true) { $phases[] = self::PHASE_UNLEASED; $phases[] = self::PHASE_EXPIRED; } $limit = $this->limit; $leased = 0; $task_ids = array(); foreach ($phases as $phase) { // NOTE: If we issue `UPDATE ... WHERE ... ORDER BY id ASC`, the query // goes very, very slowly. The `ORDER BY` triggers this, although we get // the same apparent results without it. Without the ORDER BY, binary // read slaves complain that the query isn't repeatable. To avoid both // problems, do a SELECT and then an UPDATE. $rows = queryfx_all( $conn_w, 'SELECT id, leaseOwner FROM %T %Q %Q %Q', $task_table->getTableName(), - $this->buildWhereClause($conn_w, $phase), + $this->buildCustomWhereClause($conn_w, $phase), $this->buildOrderClause($conn_w, $phase), $this->buildLimitClause($conn_w, $limit - $leased)); // NOTE: Sometimes, we'll race with another worker and they'll grab // this task before we do. We could reduce how often this happens by // selecting more tasks than we need, then shuffling them and trying // to lock only the number we're actually after. However, the amount // of time workers spend here should be very small relative to their // total runtime, so keep it simple for the moment. if ($rows) { if ($this->skipLease) { $leased += count($rows); $task_ids += array_fuse(ipull($rows, 'id')); } else { queryfx( $conn_w, 'UPDATE %T task SET leaseOwner = %s, leaseExpires = UNIX_TIMESTAMP() + %d %Q', $task_table->getTableName(), $lease_ownership_name, self::getDefaultLeaseDuration(), $this->buildUpdateWhereClause($conn_w, $phase, $rows)); $leased += $conn_w->getAffectedRows(); } if ($leased == $limit) { break; } } } if (!$leased) { return array(); } if ($this->skipLease) { $selection_condition = qsprintf( $conn_w, 'task.id IN (%Ld)', $task_ids); } else { $selection_condition = qsprintf( $conn_w, 'task.leaseOwner = %s AND leaseExpires > UNIX_TIMESTAMP()', $lease_ownership_name); } $data = queryfx_all( $conn_w, 'SELECT task.*, taskdata.data _taskData, UNIX_TIMESTAMP() _serverTime FROM %T task LEFT JOIN %T taskdata ON taskdata.id = task.dataID WHERE %Q %Q %Q', $task_table->getTableName(), $taskdata_table->getTableName(), $selection_condition, $this->buildOrderClause($conn_w, $phase), $this->buildLimitClause($conn_w, $limit)); $tasks = $task_table->loadAllFromArray($data); $tasks = mpull($tasks, null, 'getID'); foreach ($data as $row) { $tasks[$row['id']]->setServerTime($row['_serverTime']); if ($row['_taskData']) { $task_data = json_decode($row['_taskData'], true); } else { $task_data = null; } $tasks[$row['id']]->setData($task_data); } if ($this->skipLease) { // Reorder rows into the original phase order if this is a status query. $tasks = array_select_keys($tasks, $task_ids); } return $tasks; } - private function buildWhereClause(AphrontDatabaseConnection $conn_w, $phase) { + protected function buildCustomWhereClause( + AphrontDatabaseConnection $conn_w, + $phase) { + $where = array(); switch ($phase) { case self::PHASE_LEASED: $where[] = 'leaseOwner IS NOT NULL'; $where[] = 'leaseExpires >= UNIX_TIMESTAMP()'; break; case self::PHASE_UNLEASED: $where[] = 'leaseOwner IS NULL'; break; case self::PHASE_EXPIRED: $where[] = 'leaseExpires < UNIX_TIMESTAMP()'; break; default: throw new Exception("Unknown phase '{$phase}'!"); } if ($this->ids !== null) { $where[] = qsprintf($conn_w, 'id IN (%Ld)', $this->ids); } if ($this->objectPHIDs !== null) { $where[] = qsprintf($conn_w, 'objectPHID IN (%Ls)', $this->objectPHIDs); } return $this->formatWhereClause($where); } private function buildUpdateWhereClause( AphrontDatabaseConnection $conn_w, $phase, array $rows) { $where = array(); // NOTE: This is basically working around the MySQL behavior that // `IN (NULL)` doesn't match NULL. switch ($phase) { case self::PHASE_LEASED: throw new Exception( pht( 'Trying to lease tasks selected in the leased phase! This is '. 'intended to be imposssible.')); case self::PHASE_UNLEASED: $where[] = qsprintf($conn_w, 'leaseOwner IS NULL'); $where[] = qsprintf($conn_w, 'id IN (%Ld)', ipull($rows, 'id')); break; case self::PHASE_EXPIRED: $in = array(); foreach ($rows as $row) { $in[] = qsprintf( $conn_w, '(id = %d AND leaseOwner = %s)', $row['id'], $row['leaseOwner']); } $where[] = qsprintf($conn_w, '(%Q)', implode(' OR ', $in)); break; default: throw new Exception("Unknown phase '{$phase}'!"); } return $this->formatWhereClause($where); } private function buildOrderClause(AphrontDatabaseConnection $conn_w, $phase) { switch ($phase) { case self::PHASE_LEASED: // Ideally we'd probably order these by lease acquisition time, but // we don't have that handy and this is a good approximation. return qsprintf($conn_w, 'ORDER BY priority ASC, id ASC'); case self::PHASE_UNLEASED: // When selecting new tasks, we want to consume them in order of // increasing priority (and then FIFO). return qsprintf($conn_w, 'ORDER BY priority ASC, id ASC'); case self::PHASE_EXPIRED: // When selecting failed tasks, we want to consume them in roughly // FIFO order of their failures, which is not necessarily their original // queue order. // Particularly, this is important for tasks which use soft failures to // indicate that they are waiting on other tasks to complete: we need to // push them to the end of the queue after they fail, at least on // average, so we don't deadlock retrying the same blocked task over // and over again. return qsprintf($conn_w, 'ORDER BY leaseExpires ASC'); default: throw new Exception(pht('Unknown phase "%s"!', $phase)); } } private function buildLimitClause(AphrontDatabaseConnection $conn_w, $limit) { return qsprintf($conn_w, 'LIMIT %d', $limit); } private function getLeaseOwnershipName() { static $sequence = 0; // TODO: If the host name is very long, this can overflow the 64-character // column, so we pick just the first part of the host name. It might be // useful to just use a random hash as the identifier instead and put the // pid / time / host (which are somewhat useful diagnostically) elsewhere. // Likely, we could store a daemon ID instead and use that to identify // when and where code executed. See T6742. $host = php_uname('n'); $host = id(new PhutilUTF8StringTruncator()) ->setMaximumBytes(32) ->setTerminator('...') ->truncateString($host); $parts = array( getmypid(), time(), $host, ++$sequence, ); return implode(':', $parts); } } diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerTriggerQuery.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerTriggerQuery.php index 679a5236a7..eb9441aa8f 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerTriggerQuery.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerTriggerQuery.php @@ -1,233 +1,233 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withVersionBetween($min, $max) { $this->versionMin = $min; $this->versionMax = $max; return $this; } public function withNextEventBetween($min, $max) { $this->nextEpochMin = $min; $this->nextEpochMax = $max; return $this; } public function needEvents($need_events) { $this->needEvents = $need_events; return $this; } /** * Set the result order. * * Note that using `ORDER_EXECUTION` will also filter results to include only * triggers which have been scheduled to execute. You should not use this * ordering when querying for specific triggers, e.g. by ID or PHID. * * @param const Result order. * @return this */ public function setOrder($order) { $this->order = $order; return $this; } protected function nextPage(array $page) { // NOTE: We don't implement paging because we don't currently ever need // it and paging ORDER_EXCUTION is a hassle. throw new PhutilMethodNotImplementedException(); } protected function loadPage() { $task_table = new PhabricatorWorkerTrigger(); $conn_r = $task_table->establishConnection('r'); $rows = queryfx_all( $conn_r, 'SELECT t.* FROM %T t %Q %Q %Q %Q', $task_table->getTableName(), $this->buildJoinClause($conn_r), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $triggers = $task_table->loadAllFromArray($rows); if ($triggers) { if ($this->needEvents) { $ids = mpull($triggers, 'getID'); $events = id(new PhabricatorWorkerTriggerEvent())->loadAllWhere( 'triggerID IN (%Ld)', $ids); $events = mpull($events, null, 'getTriggerID'); foreach ($triggers as $key => $trigger) { $event = idx($events, $trigger->getID()); $trigger->attachEvent($event); } } foreach ($triggers as $key => $trigger) { $clock_class = $trigger->getClockClass(); if (!is_subclass_of($clock_class, 'PhabricatorTriggerClock')) { unset($triggers[$key]); continue; } try { $argv = array($trigger->getClockProperties()); $clock = newv($clock_class, $argv); } catch (Exception $ex) { unset($triggers[$key]); continue; } $trigger->attachClock($clock); } foreach ($triggers as $key => $trigger) { $action_class = $trigger->getActionClass(); if (!is_subclass_of($action_class, 'PhabricatorTriggerAction')) { unset($triggers[$key]); continue; } try { $argv = array($trigger->getActionProperties()); $action = newv($action_class, $argv); } catch (Exception $ex) { unset($triggers[$key]); continue; } $trigger->attachAction($action); } } return $triggers; } private function buildJoinClause(AphrontDatabaseConnection $conn_r) { $joins = array(); if (($this->nextEpochMin !== null) || ($this->nextEpochMax !== null) || ($this->order == PhabricatorWorkerTriggerQuery::ORDER_EXECUTION)) { $joins[] = qsprintf( $conn_r, 'JOIN %T e ON e.triggerID = t.id', id(new PhabricatorWorkerTriggerEvent())->getTableName()); } return implode(' ', $joins); } - private function buildWhereClause(AphrontDatabaseConnection $conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn_r, 't.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn_r, 't.phid IN (%Ls)', $this->phids); } if ($this->versionMin !== null) { $where[] = qsprintf( $conn_r, 't.triggerVersion >= %d', $this->versionMin); } if ($this->versionMax !== null) { $where[] = qsprintf( $conn_r, 't.triggerVersion <= %d', $this->versionMax); } if ($this->nextEpochMin !== null) { $where[] = qsprintf( $conn_r, 'e.nextEventEpoch >= %d', $this->nextEpochMin); } if ($this->nextEpochMax !== null) { $where[] = qsprintf( $conn_r, 'e.nextEventEpoch <= %d', $this->nextEpochMax); } return $this->formatWhereClause($where); } private function buildOrderClause(AphrontDatabaseConnection $conn_r) { switch ($this->order) { case self::ORDER_ID: return qsprintf( $conn_r, 'ORDER BY id DESC'); case self::ORDER_EXECUTION: return qsprintf( $conn_r, 'ORDER BY e.nextEventEpoch ASC, e.id ASC'); case self::ORDER_VERSION: return qsprintf( $conn_r, 'ORDER BY t.triggerVersion ASC'); default: throw new Exception( pht( 'Unsupported order "%s".', $this->order)); } } } diff --git a/src/infrastructure/edges/query/PhabricatorEdgeQuery.php b/src/infrastructure/edges/query/PhabricatorEdgeQuery.php index 17f1660b33..3ea7eb998e 100644 --- a/src/infrastructure/edges/query/PhabricatorEdgeQuery.php +++ b/src/infrastructure/edges/query/PhabricatorEdgeQuery.php @@ -1,332 +1,332 @@ withSourcePHIDs(array($src)) * ->withEdgeTypes(array($type)) * ->execute(); * * For more information on edges, see @{article:Using Edges}. * * @task config Configuring the Query * @task exec Executing the Query * @task internal Internal */ final class PhabricatorEdgeQuery extends PhabricatorQuery { private $sourcePHIDs; private $destPHIDs; private $edgeTypes; private $resultSet; const ORDER_OLDEST_FIRST = 'order:oldest'; const ORDER_NEWEST_FIRST = 'order:newest'; private $order = self::ORDER_NEWEST_FIRST; private $needEdgeData; /* -( Configuring the Query )---------------------------------------------- */ /** * Find edges originating at one or more source PHIDs. You MUST provide this * to execute an edge query. * * @param list List of source PHIDs. * @return this * * @task config */ public function withSourcePHIDs(array $source_phids) { $this->sourcePHIDs = $source_phids; return $this; } /** * Find edges terminating at one or more destination PHIDs. * * @param list List of destination PHIDs. * @return this * */ public function withDestinationPHIDs(array $dest_phids) { $this->destPHIDs = $dest_phids; return $this; } /** * Find edges of specific types. * * @param list List of PhabricatorEdgeConfig type constants. * @return this * * @task config */ public function withEdgeTypes(array $types) { $this->edgeTypes = $types; return $this; } /** * Configure the order edge results are returned in. * * @param const Order constant. * @return this * * @task config */ public function setOrder($order) { $this->order = $order; return $this; } /** * When loading edges, also load edge data. * * @param bool True to load edge data. * @return this * * @task config */ public function needEdgeData($need) { $this->needEdgeData = $need; return $this; } /* -( Executing the Query )------------------------------------------------ */ /** * Convenience method for loading destination PHIDs with one source and one * edge type. Equivalent to building a full query, but simplifies a common * use case. * * @param phid Source PHID. * @param const Edge type. * @return list List of destination PHIDs. */ public static function loadDestinationPHIDs($src_phid, $edge_type) { $edges = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(array($src_phid)) ->withEdgeTypes(array($edge_type)) ->execute(); return array_keys($edges[$src_phid][$edge_type]); } /** * Convenience method for loading a single edge's metadata for * a given source, destination, and edge type. Returns null * if the edge does not exist or does not have metadata. Builds * and immediately executes a full query. * * @param phid Source PHID. * @param const Edge type. * @param phid Destination PHID. * @return wild Edge annotation (or null). */ public static function loadSingleEdgeData($src_phid, $edge_type, $dest_phid) { $edges = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(array($src_phid)) ->withEdgeTypes(array($edge_type)) ->withDestinationPHIDs(array($dest_phid)) ->needEdgeData(true) ->execute(); if (isset($edges[$src_phid][$edge_type][$dest_phid]['data'])) { return $edges[$src_phid][$edge_type][$dest_phid]['data']; } return null; } /** * Load specified edges. * * @task exec */ public function execute() { if (!$this->sourcePHIDs) { throw new Exception( 'You must use withSourcePHIDs() to query edges.'); } $sources = phid_group_by_type($this->sourcePHIDs); $result = array(); // When a query specifies types, make sure we return data for all queried // types. if ($this->edgeTypes) { foreach ($this->sourcePHIDs as $phid) { foreach ($this->edgeTypes as $type) { $result[$phid][$type] = array(); } } } foreach ($sources as $type => $phids) { $conn_r = PhabricatorEdgeConfig::establishConnection($type, 'r'); $where = $this->buildWhereClause($conn_r); $order = $this->buildOrderClause($conn_r); $edges = queryfx_all( $conn_r, 'SELECT edge.* FROM %T edge %Q %Q', PhabricatorEdgeConfig::TABLE_NAME_EDGE, $where, $order); if ($this->needEdgeData) { $data_ids = array_filter(ipull($edges, 'dataID')); $data_map = array(); if ($data_ids) { $data_rows = queryfx_all( $conn_r, 'SELECT edgedata.* FROM %T edgedata WHERE id IN (%Ld)', PhabricatorEdgeConfig::TABLE_NAME_EDGEDATA, $data_ids); foreach ($data_rows as $row) { $data_map[$row['id']] = idx( json_decode($row['data'], true), 'data'); } } foreach ($edges as $key => $edge) { $edges[$key]['data'] = idx($data_map, $edge['dataID'], array()); } } foreach ($edges as $edge) { $result[$edge['src']][$edge['type']][$edge['dst']] = $edge; } } $this->resultSet = $result; return $result; } /** * Convenience function for selecting edge destination PHIDs after calling * execute(). * * Returns a flat list of PHIDs matching the provided source PHID and type * filters. By default, the filters are empty so all PHIDs will be returned. * For example, if you're doing a batch query from several sources, you might * write code like this: * * $query = new PhabricatorEdgeQuery(); * $query->setViewer($viewer); * $query->withSourcePHIDs(mpull($objects, 'getPHID')); * $query->withEdgeTypes(array($some_type)); * $query->execute(); * * // Gets all of the destinations. * $all_phids = $query->getDestinationPHIDs(); * $handles = id(new PhabricatorHandleQuery()) * ->setViewer($viewer) * ->withPHIDs($all_phids) * ->execute(); * * foreach ($objects as $object) { * // Get all of the destinations for the given object. * $dst_phids = $query->getDestinationPHIDs(array($object->getPHID())); * $object->attachHandles(array_select_keys($handles, $dst_phids)); * } * * @param list? List of PHIDs to select, or empty to select all. * @param list? List of edge types to select, or empty to select all. * @return list List of matching destination PHIDs. */ public function getDestinationPHIDs( array $src_phids = array(), array $types = array()) { if ($this->resultSet === null) { throw new Exception( 'You must execute() a query before you you can getDestinationPHIDs().'); } $result_phids = array(); $set = $this->resultSet; if ($src_phids) { $set = array_select_keys($set, $src_phids); } foreach ($set as $src => $edges_by_type) { if ($types) { $edges_by_type = array_select_keys($edges_by_type, $types); } foreach ($edges_by_type as $edges) { foreach ($edges as $edge_phid => $edge) { $result_phids[$edge_phid] = true; } } } return array_keys($result_phids); } /* -( Internals )---------------------------------------------------------- */ /** * @task internal */ - private function buildWhereClause($conn_r) { + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->sourcePHIDs) { $where[] = qsprintf( $conn_r, 'edge.src IN (%Ls)', $this->sourcePHIDs); } if ($this->edgeTypes) { $where[] = qsprintf( $conn_r, 'edge.type IN (%Ls)', $this->edgeTypes); } if ($this->destPHIDs) { // potentially complain if $this->edgeType was not set $where[] = qsprintf( $conn_r, 'edge.dst IN (%Ls)', $this->destPHIDs); } return $this->formatWhereClause($where); } /** * @task internal */ private function buildOrderClause($conn_r) { if ($this->order == self::ORDER_NEWEST_FIRST) { return 'ORDER BY edge.dateCreated DESC, edge.seq DESC'; } else { return 'ORDER BY edge.dateCreated ASC, edge.seq ASC'; } } } diff --git a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php index 7e7bfc2d35..d532d1cf0f 100644 --- a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php +++ b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php @@ -1,1108 +1,1148 @@ getResultCursor(head($page)), $this->getResultCursor(last($page)), ); } protected function getResultCursor($object) { if (!is_object($object)) { throw new Exception( pht( 'Expected object, got "%s".', gettype($object))); } return $object->getID(); } protected function nextPage(array $page) { // See getPagingViewer() for a description of this flag. $this->internalPaging = true; if ($this->beforeID !== null) { $page = array_reverse($page, $preserve_keys = true); list($before, $after) = $this->getPageCursors($page); $this->beforeID = $before; } else { list($before, $after) = $this->getPageCursors($page); $this->afterID = $after; } } final public function setAfterID($object_id) { $this->afterID = $object_id; return $this; } final protected function getAfterID() { return $this->afterID; } final public function setBeforeID($object_id) { $this->beforeID = $object_id; return $this; } final protected function getBeforeID() { return $this->beforeID; } /** * Get the viewer for making cursor paging queries. * * NOTE: You should ONLY use this viewer to load cursor objects while * building paging queries. * * Cursor paging can happen in two ways. First, the user can request a page * like `/stuff/?after=33`, which explicitly causes paging. Otherwise, we * can fall back to implicit paging if we filter some results out of a * result list because the user can't see them and need to go fetch some more * results to generate a large enough result list. * * In the first case, want to use the viewer's policies to load the object. * This prevents an attacker from figuring out information about an object * they can't see by executing queries like `/stuff/?after=33&order=name`, * which would otherwise give them a hint about the name of the object. * Generally, if a user can't see an object, they can't use it to page. * * In the second case, we need to load the object whether the user can see * it or not, because we need to examine new results. For example, if a user * loads `/stuff/` and we run a query for the first 100 items that they can * see, but the first 100 rows in the database aren't visible, we need to * be able to issue a query for the next 100 results. If we can't load the * cursor object, we'll fail or issue the same query over and over again. * So, generally, internal paging must bypass policy controls. * * This method returns the appropriate viewer, based on the context in which * the paging is occuring. * * @return PhabricatorUser Viewer for executing paging queries. */ final protected function getPagingViewer() { if ($this->internalPaging) { return PhabricatorUser::getOmnipotentUser(); } else { return $this->getViewer(); } } final protected function buildLimitClause(AphrontDatabaseConnection $conn_r) { if ($this->getRawResultLimit()) { return qsprintf($conn_r, 'LIMIT %d', $this->getRawResultLimit()); } else { return ''; } } final protected function didLoadResults(array $results) { if ($this->beforeID) { $results = array_reverse($results, $preserve_keys = true); } return $results; } final public function executeWithCursorPager(AphrontCursorPagerView $pager) { $limit = $pager->getPageSize(); $this->setLimit($limit + 1); if ($pager->getAfterID()) { $this->setAfterID($pager->getAfterID()); } else if ($pager->getBeforeID()) { $this->setBeforeID($pager->getBeforeID()); } $results = $this->execute(); $count = count($results); $sliced_results = $pager->sliceResults($results); if ($sliced_results) { list($before, $after) = $this->getPageCursors($sliced_results); if ($pager->getBeforeID() || ($count > $limit)) { $pager->setNextPageID($after); } if ($pager->getAfterID() || ($pager->getBeforeID() && ($count > $limit))) { $pager->setPrevPageID($before); } } return $sliced_results; } /** * Return the alias this query uses to identify the primary table. * * Some automatic query constructions may need to be qualified with a table * alias if the query performs joins which make column names ambiguous. If * this is the case, return the alias for the primary table the query * uses; generally the object table which has `id` and `phid` columns. * * @return string Alias for the primary table. */ protected function getPrimaryTableAlias() { return null; } protected function newResultObject() { return null; } +/* -( Building Query Clauses )--------------------------------------------- */ + + + /** + * @task clauses + */ + protected function buildWhereClause(AphrontDatabaseConnection $conn) { + throw new PhutilMethodNotImplementedException(); + } + + /* -( Paging )------------------------------------------------------------- */ + /** + * @task paging + */ protected function buildPagingClause(AphrontDatabaseConnection $conn) { $orderable = $this->getOrderableColumns(); $vector = $this->getOrderVector(); if ($this->beforeID !== null) { $cursor = $this->beforeID; $reversed = true; } else if ($this->afterID !== null) { $cursor = $this->afterID; $reversed = false; } else { // No paging is being applied to this query so we do not need to // construct a paging clause. return ''; } $keys = array(); foreach ($vector as $order) { $keys[] = $order->getOrderKey(); } $value_map = $this->getPagingValueMap($cursor, $keys); $columns = array(); foreach ($vector as $order) { $key = $order->getOrderKey(); if (!array_key_exists($key, $value_map)) { throw new Exception( pht( 'Query "%s" failed to return a value from getPagingValueMap() '. 'for column "%s".', get_class($this), $key)); } $column = $orderable[$key]; $column['value'] = $value_map[$key]; $columns[] = $column; } return $this->buildPagingClauseFromMultipleColumns( $conn, $columns, array( 'reversed' => $reversed, )); } + + /** + * @task paging + */ protected function getPagingValueMap($cursor, array $keys) { - // TODO: This is a hack to make this work with existing classes for now. return array( 'id' => $cursor, ); } + + /** + * @task paging + */ protected function loadCursorObject($cursor) { $query = newv(get_class($this), array()) ->setViewer($this->getPagingViewer()) ->withIDs(array((int)$cursor)); $this->willExecuteCursorQuery($query); $object = $query->executeOne(); if (!$object) { throw new Exception( pht( 'Cursor "%s" does not identify a valid object.', $cursor)); } return $object; } + + /** + * @task paging + */ protected function willExecuteCursorQuery( PhabricatorCursorPagedPolicyAwareQuery $query) { return; } /** * Simplifies the task of constructing a paging clause across multiple * columns. In the general case, this looks like: * * A > a OR (A = a AND B > b) OR (A = a AND B = b AND C > c) * * To build a clause, specify the name, type, and value of each column * to include: * * $this->buildPagingClauseFromMultipleColumns( * $conn_r, * array( * array( * 'table' => 't', * 'column' => 'title', * 'type' => 'string', * 'value' => $cursor->getTitle(), * 'reverse' => true, * ), * array( * 'table' => 't', * 'column' => 'id', * 'type' => 'int', * 'value' => $cursor->getID(), * ), * ), * array( * 'reversed' => $is_reversed, * )); * * This method will then return a composable clause for inclusion in WHERE. * * @param AphrontDatabaseConnection Connection query will execute on. * @param list Column description dictionaries. * @param map Additional constuction options. * @return string Query clause. + * @task paging */ final protected function buildPagingClauseFromMultipleColumns( AphrontDatabaseConnection $conn, array $columns, array $options) { foreach ($columns as $column) { PhutilTypeSpec::checkMap( $column, array( 'table' => 'optional string|null', 'column' => 'string', 'value' => 'wild', 'type' => 'string', 'reverse' => 'optional bool', 'unique' => 'optional bool', 'null' => 'optional string|null', )); } PhutilTypeSpec::checkMap( $options, array( 'reversed' => 'optional bool', )); $is_query_reversed = idx($options, 'reversed', false); $clauses = array(); $accumulated = array(); $last_key = last_key($columns); foreach ($columns as $key => $column) { $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) { case 'int': $value = qsprintf($conn, '%d', $column['value']); break; case 'float': $value = qsprintf($conn, '%f', $column['value']); break; case 'string': $value = qsprintf($conn, '%s', $column['value']); break; default: throw new Exception( pht( 'Column "%s" has unknown column type "%s".', $column['column'], $type)); } } $is_column_reversed = idx($column, 'reverse', false); $reverse = ($is_query_reversed xor $is_column_reversed); $clause = $accumulated; $table_name = idx($column, 'table'); $column_name = $column['column']; if ($table_name !== null) { $field = qsprintf($conn, '%T.%T', $table_name, $column_name); } else { $field = qsprintf($conn, '%T', $column_name); } $parts = array(); 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; } $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, '%Q %Q %Q', $field, $reverse ? '>' : '<', $value); } 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( $conn, '%Q = %Q', $field, $value); } } return '('.implode(') OR (', $clauses).')'; } /* -( Result Ordering )---------------------------------------------------- */ /** * Select a result ordering. * * This is a high-level method which selects an ordering from a predefined * list of builtin orders, as provided by @{method:getBuiltinOrders}. These * options are user-facing and not exhaustive, but are generally convenient * and meaningful. * * You can also use @{method:setOrderVector} to specify a low-level ordering * across individual orderable columns. This offers greater control but is * also more involved. * * @param string Key of a builtin order supported by this query. * @return this * @task order */ public function setOrder($order) { $orders = $this->getBuiltinOrders(); if (empty($orders[$order])) { throw new Exception( pht( 'Query "%s" does not support a builtin order "%s". Supported orders '. 'are: %s.', get_class($this), $order, implode(', ', array_keys($orders)))); } $this->builtinOrder = $order; $this->orderVector = null; return $this; } /** * Get builtin orders for this class. * * In application UIs, we want to be able to present users with a small * selection of meaningful order options (like "Order by Title") rather than * an exhaustive set of column ordering options. * * Meaningful user-facing orders are often really orders across multiple * columns: for example, a "title" ordering is usually implemented as a * "title, id" ordering under the hood. * * Builtin orders provide a mapping from convenient, understandable * user-facing orders to implementations. * * A builtin order should provide these keys: * * - `vector` (`list`): The actual order vector to use. * - `name` (`string`): Human-readable order name. * * @return map Map from builtin order keys to specification. * @task order */ public function getBuiltinOrders() { $orders = array( 'newest' => array( 'vector' => array('id'), 'name' => pht('Creation (Newest First)'), 'aliases' => array('created'), ), 'oldest' => array( 'vector' => array('-id'), 'name' => pht('Creation (Oldest First)'), ), ); $object = $this->newResultObject(); if ($object instanceof PhabricatorCustomFieldInterface) { $list = PhabricatorCustomField::getObjectFields( $object, PhabricatorCustomField::ROLE_APPLICATIONSEARCH); foreach ($list->getFields() as $field) { $index = $field->buildOrderIndex(); if (!$index) { continue; } $key = $field->getFieldKey(); $digest = $field->getFieldIndex(); $full_key = 'custom:'.$key; $orders[$full_key] = array( 'vector' => array($full_key, 'id'), 'name' => $field->getFieldName(), ); } } return $orders; } /** * Set a low-level column ordering. * * This is a low-level method which offers granular control over column * ordering. In most cases, applications can more easily use * @{method:setOrder} to choose a high-level builtin order. * * To set an order vector, specify a list of order keys as provided by * @{method:getOrderableColumns}. * * @param PhabricatorQueryOrderVector|list List of order keys. * @return this * @task order */ public function setOrderVector($vector) { $vector = PhabricatorQueryOrderVector::newFromVector($vector); $orderable = $this->getOrderableColumns(); // Make sure that all the components identify valid columns. $unique = array(); foreach ($vector as $order) { $key = $order->getOrderKey(); if (empty($orderable[$key])) { $valid = implode(', ', array_keys($orderable)); throw new Exception( pht( 'This query ("%s") does not support sorting by order key "%s". '. 'Supported orders are: %s.', get_class($this), $key, $valid)); } $unique[$key] = idx($orderable[$key], 'unique', false); } // Make sure that the last column is unique so that this is a strong // ordering which can be used for paging. $last = last($unique); if ($last !== true) { throw new Exception( pht( 'Order vector "%s" is invalid: the last column in an order must '. 'be a column with unique values, but "%s" is not unique.', $vector->getAsString(), last_key($unique))); } // Make sure that other columns are not unique; an ordering like "id, name" // does not make sense because only "id" can ever have an effect. array_pop($unique); foreach ($unique as $key => $is_unique) { if ($is_unique) { throw new Exception( pht( 'Order vector "%s" is invalid: only the last column in an order '. 'may be unique, but "%s" is a unique column and not the last '. 'column in the order.', $vector->getAsString(), $key)); } } $this->orderVector = $vector; return $this; } /** * Get the effective order vector. * * @return PhabricatorQueryOrderVector Effective vector. * @task order */ protected function getOrderVector() { if (!$this->orderVector) { if ($this->builtinOrder !== null) { $builtin_order = idx($this->getBuiltinOrders(), $this->builtinOrder); $vector = $builtin_order['vector']; } else { $vector = $this->getDefaultOrderVector(); } $vector = PhabricatorQueryOrderVector::newFromVector($vector); // We call setOrderVector() here to apply checks to the default vector. // This catches any errors in the implementation. $this->setOrderVector($vector); } return $this->orderVector; } /** * @task order */ protected function getDefaultOrderVector() { return array('id'); } /** * @task order */ public function getOrderableColumns() { $columns = array( 'id' => array( 'table' => $this->getPrimaryTableAlias(), 'column' => 'id', 'reverse' => false, 'type' => 'int', 'unique' => true, ), ); $object = $this->newResultObject(); if ($object instanceof PhabricatorCustomFieldInterface) { $list = PhabricatorCustomField::getObjectFields( $object, PhabricatorCustomField::ROLE_APPLICATIONSEARCH); foreach ($list->getFields() as $field) { $index = $field->buildOrderIndex(); if (!$index) { continue; } $key = $field->getFieldKey(); $digest = $field->getFieldIndex(); $full_key = 'custom:'.$key; $columns[$full_key] = array( 'table' => 'appsearch_order_'.$digest, 'column' => 'indexValue', 'type' => $index->getIndexValueType(), 'null' => 'tail', ); } } return $columns; } /** * @task order */ final protected function buildOrderClause(AphrontDatabaseConnection $conn) { $orderable = $this->getOrderableColumns(); $vector = $this->getOrderVector(); $parts = array(); foreach ($vector as $order) { $part = $orderable[$order->getOrderKey()]; if ($order->getIsReversed()) { $part['reverse'] = !idx($part, 'reverse', false); } $parts[] = $part; } return $this->formatOrderClause($conn, $parts); } /** * @task order */ protected function formatOrderClause( AphrontDatabaseConnection $conn, array $parts) { $is_query_reversed = false; if ($this->getBeforeID()) { $is_query_reversed = !$is_query_reversed; } $sql = array(); foreach ($parts as $key => $part) { $is_column_reversed = !empty($part['reverse']); $descending = true; if ($is_query_reversed) { $descending = !$descending; } if ($is_column_reversed) { $descending = !$descending; } $table = idx($part, 'table'); $column = $part['column']; if ($table !== null) { $field = qsprintf($conn, '%T.%T', $table, $column); } else { $field = qsprintf($conn, '%T', $column); } $null = idx($part, 'null'); 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) { $sql[] = qsprintf($conn, '%Q DESC', $field); } else { $sql[] = qsprintf($conn, '%Q ASC', $field); } } return qsprintf($conn, 'ORDER BY %Q', implode(', ', $sql)); } /* -( Application Search )------------------------------------------------- */ /** * Constrain the query with an ApplicationSearch index, requiring field values * contain at least one of the values in a set. * * This constraint can build the most common types of queries, like: * * - Find users with shirt sizes "X" or "XL". * - Find shoes with size "13". * * @param PhabricatorCustomFieldIndexStorage Table where the index is stored. * @param string|list One or more values to filter by. * @return this * @task appsearch */ public function withApplicationSearchContainsConstraint( PhabricatorCustomFieldIndexStorage $index, $value) { $this->applicationSearchConstraints[] = array( 'type' => $index->getIndexValueType(), 'cond' => '=', 'table' => $index->getTableName(), 'index' => $index->getIndexKey(), 'value' => $value, ); return $this; } /** * Constrain the query with an ApplicationSearch index, requiring values * exist in a given range. * * This constraint is useful for expressing date ranges: * * - Find events between July 1st and July 7th. * * The ends of the range are inclusive, so a `$min` of `3` and a `$max` of * `5` will match fields with values `3`, `4`, or `5`. Providing `null` for * either end of the range will leave that end of the constraint open. * * @param PhabricatorCustomFieldIndexStorage Table where the index is stored. * @param int|null Minimum permissible value, inclusive. * @param int|null Maximum permissible value, inclusive. * @return this * @task appsearch */ public function withApplicationSearchRangeConstraint( PhabricatorCustomFieldIndexStorage $index, $min, $max) { $index_type = $index->getIndexValueType(); if ($index_type != 'int') { throw new Exception( pht( 'Attempting to apply a range constraint to a field with index type '. '"%s", expected type "%s".', $index_type, 'int')); } $this->applicationSearchConstraints[] = array( 'type' => $index->getIndexValueType(), 'cond' => 'range', 'table' => $index->getTableName(), 'index' => $index->getIndexKey(), 'value' => array($min, $max), ); return $this; } /** * Order the results by an ApplicationSearch index. * * @param PhabricatorCustomField Field to which the index belongs. * @param PhabricatorCustomFieldIndexStorage Table where the index is stored. * @param bool True to sort ascending. * @return this * @task appsearch */ public function withApplicationSearchOrder( PhabricatorCustomField $field, PhabricatorCustomFieldIndexStorage $index, $ascending) { $this->applicationSearchOrders[] = array( 'key' => $field->getFieldKey(), 'type' => $index->getIndexValueType(), 'table' => $index->getTableName(), 'index' => $index->getIndexKey(), 'ascending' => $ascending, ); return $this; } /** * Get the name of the query's primary object PHID column, for constructing * JOIN clauses. Normally (and by default) this is just `"phid"`, but it may * be something more exotic. * * See @{method:getPrimaryTableAlias} if the column needs to be qualified with * a table alias. * * @return string Column name. * @task appsearch */ protected function getApplicationSearchObjectPHIDColumn() { if ($this->getPrimaryTableAlias()) { $prefix = $this->getPrimaryTableAlias().'.'; } else { $prefix = ''; } return $prefix.'phid'; } /** * Determine if the JOINs built by ApplicationSearch might cause each primary * object to return multiple result rows. Generally, this means the query * needs an extra GROUP BY clause. * * @return bool True if the query may return multiple rows for each object. * @task appsearch */ protected function getApplicationSearchMayJoinMultipleRows() { foreach ($this->applicationSearchConstraints as $constraint) { $type = $constraint['type']; $value = $constraint['value']; $cond = $constraint['cond']; switch ($cond) { case '=': switch ($type) { case 'string': case 'int': if (count((array)$value) > 1) { return true; } break; default: throw new Exception(pht('Unknown index type "%s"!', $type)); } break; case 'range': // NOTE: It's possible to write a custom field where multiple rows // match a range constraint, but we don't currently ship any in the // upstream and I can't immediately come up with cases where this // would make sense. break; default: throw new Exception(pht('Unknown constraint condition "%s"!', $cond)); } } return false; } /** * Construct a GROUP BY clause appropriate for ApplicationSearch constraints. * * @param AphrontDatabaseConnection Connection executing the query. * @return string Group clause. * @task appsearch */ protected function buildApplicationSearchGroupClause( AphrontDatabaseConnection $conn_r) { if ($this->getApplicationSearchMayJoinMultipleRows()) { return qsprintf( $conn_r, 'GROUP BY %Q', $this->getApplicationSearchObjectPHIDColumn()); } else { return ''; } } /** * Construct a JOIN clause appropriate for applying ApplicationSearch * constraints. * * @param AphrontDatabaseConnection Connection executing the query. * @return string Join clause. * @task appsearch */ protected function buildApplicationSearchJoinClause( AphrontDatabaseConnection $conn_r) { $joins = array(); foreach ($this->applicationSearchConstraints as $key => $constraint) { $table = $constraint['table']; $alias = 'appsearch_'.$key; $index = $constraint['index']; $cond = $constraint['cond']; $phid_column = $this->getApplicationSearchObjectPHIDColumn(); switch ($cond) { case '=': $type = $constraint['type']; switch ($type) { case 'string': $constraint_clause = qsprintf( $conn_r, '%T.indexValue IN (%Ls)', $alias, (array)$constraint['value']); break; case 'int': $constraint_clause = qsprintf( $conn_r, '%T.indexValue IN (%Ld)', $alias, (array)$constraint['value']); break; default: throw new Exception(pht('Unknown index type "%s"!', $type)); } $joins[] = qsprintf( $conn_r, 'JOIN %T %T ON %T.objectPHID = %Q AND %T.indexKey = %s AND (%Q)', $table, $alias, $alias, $phid_column, $alias, $index, $constraint_clause); break; case 'range': list($min, $max) = $constraint['value']; if (($min === null) && ($max === null)) { // If there's no actual range constraint, just move on. break; } if ($min === null) { $constraint_clause = qsprintf( $conn_r, '%T.indexValue <= %d', $alias, $max); } else if ($max === null) { $constraint_clause = qsprintf( $conn_r, '%T.indexValue >= %d', $alias, $min); } else { $constraint_clause = qsprintf( $conn_r, '%T.indexValue BETWEEN %d AND %d', $alias, $min, $max); } $joins[] = qsprintf( $conn_r, 'JOIN %T %T ON %T.objectPHID = %Q AND %T.indexKey = %s AND (%Q)', $table, $alias, $alias, $phid_column, $alias, $index, $constraint_clause); break; default: throw new Exception(pht('Unknown constraint condition "%s"!', $cond)); } } foreach ($this->applicationSearchOrders as $key => $order) { $table = $order['table']; $index = $order['index']; $alias = 'appsearch_order_'.$index; $phid_column = $this->getApplicationSearchObjectPHIDColumn(); $joins[] = qsprintf( $conn_r, 'LEFT JOIN %T %T ON %T.objectPHID = %Q AND %T.indexKey = %s', $table, $alias, $alias, $phid_column, $alias, $index); } return implode(' ', $joins); } + +/* -( Integration with CustomField )--------------------------------------- */ + + + /** + * @task customfield + */ protected function getPagingValueMapForCustomFields( PhabricatorCustomFieldInterface $object) { // We have to get the current field values on the cursor object. $fields = PhabricatorCustomField::getObjectFields( $object, PhabricatorCustomField::ROLE_APPLICATIONSEARCH); $fields->setViewer($this->getViewer()); $fields->readFieldsFromStorage($object); $map = array(); foreach ($fields->getFields() as $field) { $map['custom:'.$field->getFieldKey()] = $field->getValueForStorage(); } return $map; } + + /** + * @task customfield + */ protected function isCustomFieldOrderKey($key) { $prefix = 'custom:'; return !strncmp($key, $prefix, strlen($prefix)); } + }