Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'core/DataAccess/LogQueryBuilder.php')
-rw-r--r--core/DataAccess/LogQueryBuilder.php88
1 files changed, 77 insertions, 11 deletions
diff --git a/core/DataAccess/LogQueryBuilder.php b/core/DataAccess/LogQueryBuilder.php
index 186ed0d96e..7989b72e80 100644
--- a/core/DataAccess/LogQueryBuilder.php
+++ b/core/DataAccess/LogQueryBuilder.php
@@ -22,11 +22,26 @@ class LogQueryBuilder
*/
private $logTableProvider;
+ /**
+ * Forces to use a subselect when generating the query. Set value to `false` to force not using a subselect.
+ * @var string
+ */
+ private $forcedInnerGroupBy = '';
+
public function __construct(LogTablesProvider $logTablesProvider)
{
$this->logTableProvider = $logTablesProvider;
}
+ /**
+ * Forces to use a subselect when generating the query.
+ * @var string
+ */
+ public function forceInnerGroupBySubselect($innerGroupBy)
+ {
+ $this->forcedInnerGroupBy = $innerGroupBy;
+ }
+
public function getSelectQueryString(SegmentExpression $segmentExpression, $select, $from, $where, $bind, $groupBy,
$orderBy, $limitAndOffset)
{
@@ -55,11 +70,13 @@ class LogQueryBuilder
&& $fromInitially == array('log_conversion')
&& strpos($from, 'log_link_visit_action') !== false);
- if ($useSpecialConversionGroupBy) {
+ if (!empty($this->forcedInnerGroupBy)) {
+ $sql = $this->buildWrappedSelectQuery($select, $from, $where, $groupBy, $orderBy, $limitAndOffset, $tables, $this->forcedInnerGroupBy);
+ } elseif ($useSpecialConversionGroupBy) {
$innerGroupBy = "CONCAT(log_conversion.idvisit, '_' , log_conversion.idgoal, '_', log_conversion.buster)";
- $sql = $this->buildWrappedSelectQuery($select, $from, $where, $groupBy, $orderBy, $limitAndOffset, $innerGroupBy);
+ $sql = $this->buildWrappedSelectQuery($select, $from, $where, $groupBy, $orderBy, $limitAndOffset, $tables, $innerGroupBy);
} elseif ($joinWithSubSelect) {
- $sql = $this->buildWrappedSelectQuery($select, $from, $where, $groupBy, $orderBy, $limitAndOffset);
+ $sql = $this->buildWrappedSelectQuery($select, $from, $where, $groupBy, $orderBy, $limitAndOffset, $tables);
} else {
$sql = $this->buildSelectQuery($select, $from, $where, $groupBy, $orderBy, $limitAndOffset);
}
@@ -91,9 +108,20 @@ class LogQueryBuilder
* @throws Exception
* @return string
*/
- private function buildWrappedSelectQuery($select, $from, $where, $groupBy, $orderBy, $limitAndOffset, $innerGroupBy = null)
+ private function buildWrappedSelectQuery($select, $from, $where, $groupBy, $orderBy, $limitAndOffset, JoinTables $tables, $innerGroupBy = null)
{
- $matchTables = '(' . implode('|', $this->getKnownTables()) . ')';
+ $matchTables = $this->getKnownTables();
+ foreach ($tables as $table) {
+ if (is_array($table) && isset($table['tableAlias']) && !in_array($table['tableAlias'], $matchTables, $strict = true)) {
+ $matchTables[] = $table['tableAlias'];
+ } elseif (is_array($table) && isset($table['table']) && !in_array($table['table'], $matchTables, $strict = true)) {
+ $matchTables[] = $table['table'];
+ } elseif (is_string($table) && !in_array($table, $matchTables, $strict = true)) {
+ $matchTables[] = $table;
+ }
+ }
+
+ $matchTables = '(' . implode('|', $matchTables) . ')';
preg_match_all("/". $matchTables ."\.[a-z0-9_\*]+/", $select, $matches);
$neededFields = array_unique($matches[0]);
@@ -102,6 +130,30 @@ class LogQueryBuilder
. "Please use a table prefix.");
}
+ $fieldNames = array();
+ $toBeReplaced = array();
+ $epregReplace = array();
+ foreach ($neededFields as &$neededField) {
+ $parts = explode('.', $neededField);
+ if (count($parts) === 2 && !empty($parts[1])) {
+ if (in_array($parts[1], $fieldNames, $strict = true)) {
+ // eg when selecting 2 dimensions log_action_X.name
+ $columnAs = $parts[1] . md5($neededField);
+ $fieldNames[] = $columnAs;
+ // we make sure to not replace a idvisitor column when duplicate column is idvisit
+ $toBeReplaced[$neededField . ' '] = $parts[0] . '.' . $columnAs . ' ';
+ $toBeReplaced[$neededField . ')'] = $parts[0] . '.' . $columnAs . ')';
+ $toBeReplaced[$neededField . '`'] = $parts[0] . '.' . $columnAs . '`';
+ $toBeReplaced[$neededField . ','] = $parts[0] . '.' . $columnAs . ',';
+ // replace when string ends this, we need to use regex to check for this
+ $epregReplace["/(" . $neededField . ")$/"] = $parts[0] . '.' . $columnAs;
+ $neededField .= ' as ' . $columnAs;
+ } else {
+ $fieldNames[] = $parts[1];
+ }
+ }
+ }
+
preg_match_all("/". $matchTables . "/", $from, $matchesFrom);
$innerSelect = implode(", \n", $neededFields);
@@ -110,12 +162,6 @@ class LogQueryBuilder
$innerLimitAndOffset = $limitAndOffset;
- if (!isset($innerGroupBy) && in_array('log_visit', $matchesFrom[1])) {
- $innerGroupBy = "log_visit.idvisit";
- } elseif (!isset($innerGroupBy)) {
- throw new Exception('Cannot use subselect for join as no group by rule is specified');
- }
-
$innerOrderBy = "NULL";
if ($innerLimitAndOffset && $orderBy) {
// only When LIMITing we can apply to the inner query the same ORDER BY as the parent query
@@ -126,9 +172,29 @@ class LogQueryBuilder
$innerGroupBy = false;
}
+ if (!isset($innerGroupBy) && in_array('log_visit', $matchesFrom[1])) {
+ $innerGroupBy = "log_visit.idvisit";
+ } elseif (!isset($innerGroupBy)) {
+ throw new Exception('Cannot use subselect for join as no group by rule is specified');
+ }
+
+ if (!empty($toBeReplaced)) {
+ $select = preg_replace(array_keys($epregReplace), array_values($epregReplace), $select);
+ $select = str_replace(array_keys($toBeReplaced), array_values($toBeReplaced), $select);
+ if (!empty($groupBy)) {
+ $groupBy = preg_replace(array_keys($epregReplace), array_values($epregReplace), $groupBy);
+ $groupBy = str_replace(array_keys($toBeReplaced), array_values($toBeReplaced), $groupBy);
+ }
+ if (!empty($orderBy)) {
+ $orderBy = preg_replace(array_keys($epregReplace), array_values($epregReplace), $orderBy);
+ $orderBy = str_replace(array_keys($toBeReplaced), array_values($toBeReplaced), $orderBy);
+ }
+ }
+
$innerQuery = $this->buildSelectQuery($innerSelect, $innerFrom, $innerWhere, $innerGroupBy, $innerOrderBy, $innerLimitAndOffset);
$select = preg_replace('/'.$matchTables.'\./', 'log_inner.', $select);
+
$from = "
(
$innerQuery