diff --git a/CRM/Campaign/Selector/Search.php b/CRM/Campaign/Selector/Search.php index 8d71c4cd1e1e..ec046e6ab360 100644 --- a/CRM/Campaign/Selector/Search.php +++ b/CRM/Campaign/Selector/Search.php @@ -273,10 +273,10 @@ public function buildPrevNextCache($sort) { $cacheKey = "civicrm search {$this->_key}"; Civi::service('prevnext')->deleteItem(NULL, $cacheKey, 'civicrm_contact'); - $sql = $this->_query->searchQuery(0, 0, $sort, + $sql = $this->_query->getSearchSQL(0, 0, $sort, FALSE, FALSE, FALSE, FALSE, - TRUE, $this->_campaignWhereClause, + $this->_campaignWhereClause, NULL, $this->_campaignFromClause ); diff --git a/CRM/Contact/BAO/Query.php b/CRM/Contact/BAO/Query.php index 8cc48504053b..703ec0dd2eb2 100644 --- a/CRM/Contact/BAO/Query.php +++ b/CRM/Contact/BAO/Query.php @@ -4880,95 +4880,7 @@ public function searchQuery( $additionalFromClause = NULL, $skipOrderAndLimit = FALSE ) { - if ($includeContactIds) { - $this->_includeContactIds = TRUE; - $this->_whereClause = $this->whereClause(); - } - - $onlyDeleted = in_array(array('deleted_contacts', '=', '1', '0', '0'), $this->_params); - - // if we’re explicitly looking for a certain contact’s contribs, events, etc. - // and that contact happens to be deleted, set $onlyDeleted to true - foreach ($this->_params as $values) { - $name = CRM_Utils_Array::value(0, $values); - $op = CRM_Utils_Array::value(1, $values); - $value = CRM_Utils_Array::value(2, $values); - if ($name == 'contact_id' and $op == '=') { - if (CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $value, 'is_deleted')) { - $onlyDeleted = TRUE; - } - break; - } - } - - // building the query string - $groupBy = $groupByCols = NULL; - if (!$count) { - if (isset($this->_groupByComponentClause)) { - $groupByCols = preg_replace('/^GROUP BY /', '', trim($this->_groupByComponentClause)); - $groupByCols = explode(', ', $groupByCols); - } - elseif ($this->_useGroupBy) { - $groupByCols = array('contact_a.id'); - } - } - if ($this->_mode & CRM_Contact_BAO_Query::MODE_ACTIVITY && (!$count)) { - $groupByCols = array('civicrm_activity.id'); - } - if (!empty($groupByCols)) { - $groupBy = " GROUP BY " . implode(', ', $groupByCols); - } - - $order = $orderBy = $limit = ''; - if (!$count) { - list($order, $additionalFromClause) = $this->prepareOrderBy($sort, $sortByChar, $sortOrder, $additionalFromClause); - - if ($rowCount > 0 && $offset >= 0) { - $offset = CRM_Utils_Type::escape($offset, 'Int'); - $rowCount = CRM_Utils_Type::escape($rowCount, 'Int'); - $limit = " LIMIT $offset, $rowCount "; - } - } - // Two cases where we are disabling FGB (FULL_GROUP_BY_MODE): - // 1. Expecting the search query to return all the first single letter characters of contacts ONLY, but when FGB is enabled - // MySQL expect the columns present in GROUP BY, must be present in SELECT clause and that results into error, needless to have other columns. - // 2. When GROUP BY columns are present then disable FGB otherwise it demands to add ORDER BY columns in GROUP BY and eventually in SELECT - // clause. This will impact the search query output. - $disableFullGroupByMode = ($sortByChar || !empty($groupBy) || $groupContacts); - - if ($disableFullGroupByMode) { - CRM_Core_DAO::disableFullGroupByMode(); - } - - // CRM-15231 - $this->_sort = $sort; - - //CRM-15967 - $this->includePseudoFieldsJoin($sort); - - list($select, $from, $where, $having) = $this->query($count, $sortByChar, $groupContacts, $onlyDeleted); - - if ($additionalWhereClause) { - $where = $where . ' AND ' . $additionalWhereClause; - } - - //additional from clause should be w/ proper joins. - if ($additionalFromClause) { - $from .= "\n" . $additionalFromClause; - } - - // if we are doing a transform, do it here - // use the $from, $where and $having to get the contact ID - if ($this->_displayRelationshipType) { - $this->filterRelatedContacts($from, $where, $having); - } - - if ($skipOrderAndLimit) { - $query = "$select $from $where $having $groupBy"; - } - else { - $query = "$select $from $where $having $groupBy $order $limit"; - } + $query = $this->getSearchSQL($offset, $rowCount, $sort, $count, $includeContactIds, $sortByChar, $groupContacts, $additionalWhereClause, $sortOrder, $additionalFromClause, $skipOrderAndLimit); if ($returnQuery) { return $query; @@ -4979,9 +4891,8 @@ public function searchQuery( $dao = CRM_Core_DAO::executeQuery($query); - if ($disableFullGroupByMode) { - CRM_Core_DAO::reenableFullGroupByMode(); - } + // We can always call this - it will only re-enable if it was originally enabled. + CRM_Core_DAO::reenableFullGroupByMode(); if ($groupContacts) { $ids = array(); @@ -6658,4 +6569,135 @@ protected function addBasicCancelStatsToSummary(&$summary, $where, $from) { } } + /** + * /** + * Create the sql query for an contact search. + * + * @param int $offset + * The offset for the query. + * @param int $rowCount + * The number of rows to return. + * @param string|CRM_Utils_Sort $sort + * The order by string. + * @param bool $count + * Is this a count only query ?. + * @param bool $includeContactIds + * Should we include contact ids?. + * @param bool $sortByChar + * If true returns the distinct array of first characters for search results. + * @param bool $groupContacts + * If true, return only the contact ids. + * @param string $additionalWhereClause + * If the caller wants to further restrict the search (used for components). + * @param null $sortOrder + * @param string $additionalFromClause + * Should be clause with proper joins, effective to reduce where clause load. + * + * @param bool $skipOrderAndLimit + * @return string + */ + public function getSearchSQL( + $offset = 0, $rowCount = 0, $sort = NULL, + $count = FALSE, $includeContactIds = FALSE, + $sortByChar = FALSE, $groupContacts = FALSE, + $additionalWhereClause = NULL, $sortOrder = NULL, + $additionalFromClause = NULL, $skipOrderAndLimit = FALSE) { + if ($includeContactIds) { + $this->_includeContactIds = TRUE; + $this->_whereClause = $this->whereClause(); + } + + $onlyDeleted = in_array([ + 'deleted_contacts', + '=', + '1', + '0', + '0' + ], $this->_params); + + // if we’re explicitly looking for a certain contact’s contribs, events, etc. + // and that contact happens to be deleted, set $onlyDeleted to true + foreach ($this->_params as $values) { + $name = CRM_Utils_Array::value(0, $values); + $op = CRM_Utils_Array::value(1, $values); + $value = CRM_Utils_Array::value(2, $values); + if ($name == 'contact_id' and $op == '=') { + if (CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $value, 'is_deleted')) { + $onlyDeleted = TRUE; + } + break; + } + } + + // building the query string + $groupBy = $groupByCols = NULL; + if (!$count) { + if (isset($this->_groupByComponentClause)) { + $groupByCols = preg_replace('/^GROUP BY /', '', trim($this->_groupByComponentClause)); + $groupByCols = explode(', ', $groupByCols); + } + elseif ($this->_useGroupBy) { + $groupByCols = ['contact_a.id']; + } + } + if ($this->_mode & CRM_Contact_BAO_Query::MODE_ACTIVITY && (!$count)) { + $groupByCols = ['civicrm_activity.id']; + } + if (!empty($groupByCols)) { + $groupBy = " GROUP BY " . implode(', ', $groupByCols); + } + + $order = $orderBy = $limit = ''; + if (!$count) { + list($order, $additionalFromClause) = $this->prepareOrderBy($sort, $sortByChar, $sortOrder, $additionalFromClause); + + if ($rowCount > 0 && $offset >= 0) { + $offset = CRM_Utils_Type::escape($offset, 'Int'); + $rowCount = CRM_Utils_Type::escape($rowCount, 'Int'); + $limit = " LIMIT $offset, $rowCount "; + } + } + // Two cases where we are disabling FGB (FULL_GROUP_BY_MODE): + // 1. Expecting the search query to return all the first single letter characters of contacts ONLY, but when FGB is enabled + // MySQL expect the columns present in GROUP BY, must be present in SELECT clause and that results into error, needless to have other columns. + // 2. When GROUP BY columns are present then disable FGB otherwise it demands to add ORDER BY columns in GROUP BY and eventually in SELECT + // clause. This will impact the search query output. + $disableFullGroupByMode = ($sortByChar || !empty($groupBy) || $groupContacts); + + if ($disableFullGroupByMode) { + CRM_Core_DAO::disableFullGroupByMode(); + } + + // CRM-15231 + $this->_sort = $sort; + + //CRM-15967 + $this->includePseudoFieldsJoin($sort); + + list($select, $from, $where, $having) = $this->query($count, $sortByChar, $groupContacts, $onlyDeleted); + + if ($additionalWhereClause) { + $where = $where . ' AND ' . $additionalWhereClause; + } + + //additional from clause should be w/ proper joins. + if ($additionalFromClause) { + $from .= "\n" . $additionalFromClause; + } + + // if we are doing a transform, do it here + // use the $from, $where and $having to get the contact ID + if ($this->_displayRelationshipType) { + $this->filterRelatedContacts($from, $where, $having); + } + + if ($skipOrderAndLimit) { + $query = "$select $from $where $having $groupBy"; + } + else { + $query = "$select $from $where $having $groupBy $order $limit"; + } + return $query; + } + }