Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove slow join from activity search #17250

Merged
merged 1 commit into from
Jun 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions CRM/Activity/BAO/Activity.php
Original file line number Diff line number Diff line change
Expand Up @@ -905,13 +905,15 @@ public static function filterActivityTypes($params) {
*/
public function addSelectWhereClause() {
$clauses = [];
// @todo - check if $permissedActivityTYpes === all activity types and do not add critieria if so.
$permittedActivityTypeIDs = self::getPermittedActivityTypes();
$allActivityTypes = self::buildOptions('activity_type_id');
if (empty($permittedActivityTypeIDs)) {
// This just prevents a mysql fail if they have no access - should be extremely edge case.
$permittedActivityTypeIDs = [0];
}
$clauses['activity_type_id'] = ('IN (' . implode(', ', $permittedActivityTypeIDs) . ')');
if (array_keys($allActivityTypes) !== array_keys($permittedActivityTypeIDs)) {
$clauses['activity_type_id'] = ('IN (' . implode(', ', $permittedActivityTypeIDs) . ')');
}

$contactClause = CRM_Utils_SQL::mergeSubquery('Contact');
if ($contactClause) {
Expand Down Expand Up @@ -2131,7 +2133,7 @@ public static function exportableFields($name = 'Activity') {

// TODO: ideally we should retrieve all fields from xml, in this case since activity processing is done
// my case hence we have defined fields as case_*
if ($name == 'Activity') {
if ($name === 'Activity') {
$exportableFields = CRM_Activity_DAO_Activity::export();
$exportableFields['source_contact_id'] = [
'title' => ts('Source Contact ID'),
Expand Down
17 changes: 4 additions & 13 deletions CRM/Activity/BAO/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,13 @@ public static function select(&$query) {
$query->_tables['civicrm_activity'] = $query->_whereTables['civicrm_activity'] = 1;
}

if (!empty($query->_returnProperties['activity_type_id'])) {
$query->_select['activity_type_id'] = 'activity_type.value as activity_type_id';
if (!empty($query->_returnProperties['activity_type_id'])
|| !empty($query->_returnProperties['activity_type'])
) {
$query->_select['activity_type_id'] = 'civicrm_activity.activity_type_id';
$query->_element['activity_type_id'] = 1;
$query->_tables['civicrm_activity'] = 1;
$query->_tables['activity_type'] = 1;
$query->_whereTables['civicrm_activity'] = 1;
$query->_whereTables['activity_type'] = 1;
}

if (!empty($query->_returnProperties['activity_type'])) {
$query->_select['activity_type'] = 'activity_type.label as activity_type';
$query->_element['activity_type'] = 1;
$query->_tables['civicrm_activity'] = 1;
$query->_tables['activity_type'] = 1;
$query->_whereTables['civicrm_activity'] = 1;
$query->_whereTables['activity_type'] = 1;
}

if (!empty($query->_returnProperties['activity_subject'])) {
Expand Down
31 changes: 0 additions & 31 deletions CRM/Activity/Selector/Search.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,37 +154,6 @@ public function __construct(

$this->_activityClause = $activityClause;

// CRM-12675
$components = CRM_Core_Component::getNames();
$componentClause = [];
foreach ($components as $componentID => $componentName) {
// CRM-19201: Add support for searching CiviCampaign and CiviCase
// activities. For CiviCase, "access all cases and activities" is
// required here rather than "access my cases and activities" to
// prevent those with only the later permission from seeing a list
// of all cases which might present a privacy issue.
// @todo this is the cause of the current devastatingly bad performance on
// activity search - it involves a bad join.
// The correct fix is to use the permission infrastrucutre - ie. add in the
// clause generated by CRM_Activity_BAO_Query::addSelectWhere
// but some testing needs to check that before making the change
// see https://github.com/civicrm/civicrm-core/blob/be2fb01f90f5f299dd07402a41fed7c7c7567f00/CRM/Utils/SQL.php#L48
// for how it's done in the api kernel.
if (!CRM_Core_Permission::access($componentName, TRUE, TRUE)) {
$componentClause[] = " (activity_type.component_id IS NULL OR activity_type.component_id <> {$componentID}) ";
}
}

if (!empty($componentClause)) {
$componentRestriction = implode(' AND ', $componentClause);
if (empty($this->_activityClause)) {
$this->_activityClause = $componentRestriction;
}
else {
$this->_activityClause .= ' AND ' . $componentRestriction;
}
}

// type of selector
$this->_action = $action;
$this->_query = new CRM_Contact_BAO_Query($this->_queryParams,
Expand Down
22 changes: 22 additions & 0 deletions CRM/Contact/BAO/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -5047,6 +5047,28 @@ public function generatePermissionClause($onlyDeleted = FALSE, $count = FALSE) {
$this->_skipDeleteClause
);

if (isset($this->_tables['civicrm_activity'])) {
$bao = new CRM_Activity_BAO_Activity();
$clauses = $subclauses = [];
foreach ((array) $bao->addSelectWhereClause() as $field => $vals) {
if ($vals && $field !== 'id') {
$clauses[] = $bao->tableName() . ".$field " . $vals;
}
elseif ($vals) {
$subclauses[] = "$field " . implode(" AND $field ", (array) $vals);
}
}
if ($subclauses) {
$clauses[] = $bao->tableName() . '.`id` IN (SELECT `id` FROM `' . $bao->tableName() . '` WHERE ' . implode(' AND ', $subclauses) . ')';
}
if (!empty($clauses) && $this->_permissionWhereClause) {
$this->_permissionWhereClause .= ' AND (' . implode(' AND ', $clauses) . ')';
}
elseif (!empty($clauses)) {
$this->_permissionWhereClause .= '(' . implode(' AND ', $clauses) . ')';
}
}

// regenerate fromClause since permission might have added tables
if ($this->_permissionWhereClause) {
//fix for row count in qill (in contribute/membership find)
Expand Down
4 changes: 2 additions & 2 deletions CRM/Core/DAO.php
Original file line number Diff line number Diff line change
Expand Up @@ -2640,7 +2640,7 @@ public function getOptionLabels() {
*
* @param string $context
*
* @throws Exception
* @throws CRM_Core_Exception
* @return array
*/
public static function buildOptionsContext($context = NULL) {
Expand All @@ -2654,7 +2654,7 @@ public static function buildOptionsContext($context = NULL) {
];
// Validation: enforce uniformity of this param
if ($context !== NULL && !isset($contexts[$context])) {
throw new Exception("'$context' is not a valid context for buildOptions.");
throw new CRM_Core_Exception("'$context' is not a valid context for buildOptions.");
}
return $contexts;
}
Expand Down
2 changes: 1 addition & 1 deletion CRM/Export/BAO/ExportProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,7 @@ public function getExportFileName() {
* @return string
*/
public function getHeaderForRow($field) {
if (substr($field, -11) == 'campaign_id') {
if (substr($field, -11) === 'campaign_id') {
// @todo - set this correctly in the xml rather than here.
// This will require a generalised handling cleanup
return ts('Campaign ID');
Expand Down
3 changes: 3 additions & 0 deletions tests/phpunit/CRM/Activity/Selector/SearchTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ class CRM_Activity_Selector_SearchTest extends CiviUnitTestCase {

/**
* Test activity search applies a permission based component filter.
*
* @throws \CRM_Core_Exception
* @throws \CiviCRM_API3_Exception
*/
public function testActivitySearchComponentPermission() {
$this->activityCreate(['activity_type_id' => 'Contribution']);
Expand Down
12 changes: 6 additions & 6 deletions tests/phpunit/CRM/Contact/SelectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,24 +55,24 @@ public function testSelectorQuery($dataSet) {
$this->assertLike($this->strWrangle($queryString), $this->strWrangle($sql[$index]));
}
// Ensure that search builder return individual contact as per criteria
if ($dataSet['context'] == 'builder') {
if ($dataSet['context'] === 'builder') {
$contactID = $this->individualCreate(['first_name' => 'James', 'last_name' => 'Bond']);
if ('Search builder behaviour for Activity' == $dataSet['description']) {
if ('Search builder behaviour for Activity' === $dataSet['description']) {
$this->callAPISuccess('Activity', 'create', [
'activity_type_id' => 'Meeting',
'subject' => "Test",
'subject' => 'Test',
'source_contact_id' => $contactID,
]);
$rows = CRM_Core_DAO::executeQuery(implode(' ', $sql))->fetchAll();
$this->assertEquals(1, count($rows));
$this->assertCount(1, $rows);
$this->assertEquals($contactID, $rows[0]['source_contact_id']);
}
else {
$this->callAPISuccess('Address', 'create', [
'contact_id' => $contactID,
'location_type_id' => "Home",
'location_type_id' => 'Home',
'is_primary' => 1,
'country_id' => "IN",
'country_id' => 'IN',
]);
$rows = $selector->getRows(CRM_Core_Action::VIEW, 0, 50, '');
$this->assertEquals(1, count($rows));
Expand Down