1
0
قرینه از https://github.com/matomo-org/matomo.git synced 2025-08-25 08:27:37 +00:00
Files
matomo/tests/PHPUnit/Unit/DataAccess/LogQueryBuilder/JoinGeneratorTest.php
Jacob R b8cbe71a76 Added the ability to force the use of an index (#22559)
* Added the ability to force the use of an index

* Improving test a little more

* Make suggested comment correction

Co-authored-by: Michal Kleiner <michal@innocraft.com>

* Make suggested comment correction

Co-authored-by: Michal Kleiner <michal@innocraft.com>

* Adding new config to control use of new feature

* Fixing test after recent change

* Fixing test to avoid affecting other tests

* Fixing PHPCS error

* Applying some PR review feedback from Cloud team

* Fixing edge case

* Adding test case around regression found in patch on Cloud

* Making slight test case improvement

* Removed config for forcing index

* Removed unused import

* Remove no longer needed code

* remove unneeded use statement

---------

Co-authored-by: Michal Kleiner <michal@innocraft.com>
Co-authored-by: Stefan Giehl <stefan@matomo.org>
2024-10-22 16:02:45 +02:00

302 خطوط
14 KiB
PHP

<?php
/**
* Matomo - free/libre analytics platform
*
* @link https://matomo.org
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\Tests\Unit\DataAccess;
use Piwik\DataAccess\LogQueryBuilder\JoinGenerator;
use Piwik\DataAccess\LogQueryBuilder\JoinTables;
use Piwik\Tests\Framework\Mock\Plugin\LogTablesProvider;
/**
* @group Core
*/
class JoinGeneratorTest extends \PHPUnit\Framework\TestCase
{
/**
* @var JoinGenerator
*/
private $generator;
public function setUp(): void
{
$this->generator = $this->makeTables(array(
'log_visit',
array('table' => 'log_conversion', 'joinOn' => 'log_conversion.idvisit = log_visit.idvisit'),
'log_action'));
}
public function testConstructorShouldAddTablesIfNeeded()
{
$tables = $this->makeTables(array('log_visit', 'log_action'));
$this->makeGenerator($tables);
$this->assertEquals(array('log_visit', 'log_action', 'log_link_visit_action'), $tables->getTables());
}
public function testGenerateShouldJoinWithSubselectIfBaseTableIsLogVisit()
{
$generator = $this->generate(array('log_visit', 'log_action'));
$this->assertTrue($generator->shouldJoinWithSelect());
}
public function testGenerateShouldNotJoinWithSubselectIfBaseTableIsLogVisitButNoTableToJoin()
{
$generator = $this->generate(array('log_visit'));
$this->assertFalse($generator->shouldJoinWithSelect());
}
public function testGenerateShouldNotJoinWithSubselectIfLogVisitIsGivenButItIsNotBaseTable()
{
$generator = $this->generate(array('log_conversion', 'log_visit'));
$this->assertFalse($generator->shouldJoinWithSelect());
}
public function testGenerateGetJoinString()
{
$generator = $this->generate(array('log_action', 'log_link_visit_action', 'log_visit'));
$expected = 'log_action AS log_action ';
$expected .= 'LEFT JOIN log_link_visit_action AS log_link_visit_action ON log_link_visit_action.idaction_url = log_action.idaction ';
$expected .= 'LEFT JOIN log_visit AS log_visit ON log_visit.idvisit = log_link_visit_action.idvisit';
$this->assertEquals($expected, $generator->getJoinString());
}
public function testGenerateGetJoinStringOnlyOneActionTable()
{
$generator = $this->generate(array('log_action'));
$this->assertEquals('log_action AS log_action', $generator->getJoinString());
}
public function testGenerateGetJoinStringOnlyActionTables()
{
$generator = $this->generate(array('log_link_visit_action', 'log_action'));
$expected = 'log_link_visit_action AS log_link_visit_action';
$expected .= ' LEFT JOIN log_action AS log_action ON log_link_visit_action.idaction_url = log_action.idaction';
$this->assertEquals($expected, $generator->getJoinString());
}
public function testGenerateGetJoinStringJoinCustomVisitTable()
{
$generator = $this->generate(array('log_visit', 'log_custom'));
$this->assertEquals('log_visit AS log_visit LEFT JOIN log_custom AS log_custom ON `log_custom`.`user_id` = `log_visit`.`user_id`', $generator->getJoinString());
}
public function testGenerateGetJoinStringJoinMultipleCustomVisitTable()
{
$generator = $this->generate(array('log_visit', 'log_custom_other', 'log_custom'));
$this->assertEquals('log_visit AS log_visit LEFT JOIN log_custom AS log_custom ON `log_custom`.`user_id` = `log_visit`.`user_id` LEFT JOIN log_custom_other AS log_custom_other ON `log_custom_other`.`other_id` = `log_custom`.`other_id`', $generator->getJoinString());
}
public function testGenerateGetJoinStringJoinMultipleCustomVisitTableWithMissingOne()
{
$generator = $this->generate(array('log_visit', 'log_custom_other'));
$this->assertEquals('log_visit AS log_visit LEFT JOIN log_custom AS log_custom ON `log_custom`.`user_id` = `log_visit`.`user_id` LEFT JOIN log_custom_other AS log_custom_other ON `log_custom_other`.`other_id` = `log_custom`.`other_id`', $generator->getJoinString());
}
/**
* Note: the exception reports `log_visit` and not `log_custom` as it resolves the dependencies as so resolves
* from `log_custom` to `log_visit` but is then not able to find a way to join `log_visit` with `log_action`
*/
public function testGenerateGetJoinStringCustomVisitTableCantBeJoinedWithAction()
{
$this->expectException(\Exception::class);
$this->expectExceptionMessage('Table \'log_visit\' can\'t be joined for segmentation');
$generator = $this->generate(array('log_action', 'log_custom'));
$generator->getJoinString();
}
public function testGenerateGetJoinStringJoinCustomVisitTableMultiple()
{
$generator = $this->generate(array('log_visit', 'log_action', 'log_custom'));
$this->assertEquals('log_visit AS log_visit LEFT JOIN log_link_visit_action AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit LEFT JOIN log_action AS log_action ON log_link_visit_action.idaction_url = log_action.idaction LEFT JOIN log_custom AS log_custom ON `log_custom`.`user_id` = `log_visit`.`user_id`', $generator->getJoinString());
}
public function testGenerateGetJoinStringManuallyJoinedAlready()
{
$generator = $this->generate(array(
'log_link_visit_action',
array('table' => 'log_visit', 'joinOn' => 'log_visit.idvisit = log_link_visit_action.idvisit'),
array('table' => 'log_action', 'joinOn' => 'log_link_visit_action.idaction_name = log_action.idaction'),
'log_action'
));
$expected = 'log_link_visit_action AS log_link_visit_action ';
$expected .= 'LEFT JOIN log_visit AS log_visit ON log_visit.idvisit = log_link_visit_action.idvisit ';
$expected .= 'LEFT JOIN log_action AS log_action ON (log_link_visit_action.idaction_name = log_action.idaction AND log_link_visit_action.idaction_url = log_action.idaction)';
$this->assertEquals($expected, $generator->getJoinString());
}
public function testGenerateGetJoinStringManuallyJoinedAlreadyWithCustomConditionInArray()
{
$generator = $this->generate(array(
'log_visit',
array('table' => 'log_conversion', 'joinOn' => 'log_visit.idvisit2 = log_conversion.idvisit2'),
'log_conversion'
));
$expected = 'log_visit AS log_visit ';
$expected .= 'LEFT JOIN log_conversion AS log_conversion ON log_visit.idvisit2 = log_conversion.idvisit2';
$this->assertEquals($expected, $generator->getJoinString());
}
public function testGenerateGetJoinStringManuallyJoinedAlreadyWithCustomConditionInArrayAndFurtherTablesAfterwards()
{
$generator = $this->generate(array(
'log_visit',
array('table' => 'log_conversion', 'joinOn' => 'log_visit.idvisit2 = log_conversion.idvisit2'),
'log_conversion',
'log_link_visit_action'
));
$expected = 'log_visit AS log_visit ';
$expected .= 'LEFT JOIN log_conversion AS log_conversion ON log_visit.idvisit2 = log_conversion.idvisit2 ';
$expected .= 'LEFT JOIN log_link_visit_action AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit';
$this->assertEquals($expected, $generator->getJoinString());
}
public function testGenerateGetJoinStringManuallyJoinedAlreadyWithCustomConditionInArrayInverted()
{
$this->expectException(\Exception::class);
$this->expectExceptionMessage('Please reorganize the joined tables as the table log_conversion in {"0":"log_visit","1":"log_conversion","2":"log_link_visit_action","3":{"table":"log_conversion","joinOn":"log_link_visit_action.idvisit2 = log_conversion.idvisit2"}} cannot be joined correctly.');
$generator = $this->generate(array(
'log_visit',
'log_conversion',
'log_link_visit_action',
array('table' => 'log_conversion', 'joinOn' => 'log_link_visit_action.idvisit2 = log_conversion.idvisit2'),
));
$expected = 'log_visit AS log_visit ';
$expected .= 'LEFT JOIN log_conversion AS log_conversion ON log_visit.idvisit2 = log_conversion.idvisit2 ';
$expected .= 'LEFT JOIN log_link_visit_action AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit ';
$expected .= 'LEFT JOIN log_conversion AS log_conversion ON log_conversion.idvisit = log_visit.idvisit ';
$expected .= 'LEFT JOIN log_conversion AS log_conversion ON log_visit.idvisit2 = log_conversion.idvisit2 ';
$expected .= 'LEFT JOIN log_link_visit_action AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit';
$this->assertEquals($expected, $generator->getJoinString());
}
public function testGenerateGetJoinStringManuallyJoinedAlreadyPlusCustomJoinButAlsoLeft()
{
$generator = $this->generate(array(
'log_link_visit_action',
array('table' => 'log_visit', 'joinOn' => 'log_visit.idvisit = log_link_visit_action.idvisit'),
array('table' => 'log_action', 'join' => 'LeFt JOIN', 'joinOn' => 'log_link_visit_action.idaction_name = log_action.idaction'),
'log_action'
));
$expected = 'log_link_visit_action AS log_link_visit_action ';
$expected .= 'LEFT JOIN log_visit AS log_visit ON log_visit.idvisit = log_link_visit_action.idvisit ';
$expected .= 'LEFT JOIN log_action AS log_action ON (log_link_visit_action.idaction_name = log_action.idaction AND log_link_visit_action.idaction_url = log_action.idaction)';
$this->assertEquals($expected, $generator->getJoinString());
}
public function testGenerateGetJoinStringManuallyJoinedAlreadyPlusCustomJoinButAlsoLeftNeedsKeepOrder()
{
$generator = $this->generate(array(
'log_visit',
array('table' => 'log_link_visit_action', 'join' => 'RIGHT JOIN'),
'log_action'
));
$expected = 'log_visit AS log_visit ';
$expected .= 'RIGHT JOIN log_link_visit_action AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit ';
$expected .= 'LEFT JOIN log_action AS log_action ON log_link_visit_action.idaction_url = log_action.idaction';
$this->assertEquals($expected, $generator->getJoinString());
}
public function testGenerateGetJoinStringForceIndexUse()
{
$generator = $this->generate([
['table' => 'log_visit', 'useIndex' => 'index_idsite_datetime'],
['table' => 'log_link_visit_action', 'join' => 'RIGHT JOIN'],
'log_action'
]);
$expected = 'log_visit AS log_visit USE INDEX (index_idsite_datetime) ';
$expected .= 'RIGHT JOIN log_link_visit_action AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit ';
$expected .= 'LEFT JOIN log_action AS log_action ON log_link_visit_action.idaction_url = log_action.idaction';
$this->assertEquals($expected, $generator->getJoinString());
}
public function testGenerateGetJoinStringManuallyJoinedAlreadyPlusCustomJoinAtEndButAlsoLeftNeedsKeepOrder()
{
$generator = $this->generate(array(
'log_visit',
'log_action',
array('table' => 'log_link_visit_action', 'join' => 'RIGHT JOIN'),
));
$expected = 'log_visit AS log_visit ';
$expected .= 'RIGHT JOIN log_link_visit_action AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit ';
$expected .= 'LEFT JOIN log_action AS log_action ON log_link_visit_action.idaction_url = log_action.idaction';
$this->assertEquals($expected, $generator->getJoinString());
}
public function testGenerateGetJoinStringManualJoin()
{
$generator = $this->generate(array(
'log_link_visit_action',
array('table' => 'log_visit',
'join' => 'RIGHT JOIN','joinOn' => 'log_visit.idvisit = log_link_visit_action.idvisit'),
array('table' => 'log_action',
'tableAlias' => 'log_action_r',
'join' => 'RIGHT JOIN',
'joinOn' => 'log_link_visit_action.idaction_test = log_action_r.idaction'),
'log_action'
));
$expected = 'log_link_visit_action AS log_link_visit_action ';
$expected .= 'RIGHT JOIN log_visit AS log_visit ON log_visit.idvisit = log_link_visit_action.idvisit ';
$expected .= 'RIGHT JOIN log_action AS log_action_r ON log_link_visit_action.idaction_test = log_action_r.idaction ';
$expected .= 'LEFT JOIN log_action AS log_action ON log_link_visit_action.idaction_url = log_action.idaction';
$this->assertEquals($expected, $generator->getJoinString());
}
public function testGenerateGetJoinStringAllTables()
{
$generator = $this->generate(array(
'log_action',
'log_conversion_item',
'log_link_visit_action',
'log_conversion',
'log_visit',
));
$expected = 'log_action AS log_action ';
$expected .= 'LEFT JOIN log_link_visit_action AS log_link_visit_action ON log_link_visit_action.idaction_url = log_action.idaction ';
$expected .= 'LEFT JOIN log_visit AS log_visit ON log_visit.idvisit = log_link_visit_action.idvisit ';
$expected .= 'LEFT JOIN log_conversion AS log_conversion ON log_conversion.idvisit = log_link_visit_action.idvisit ';
$expected .= 'LEFT JOIN log_conversion_item AS log_conversion_item ON log_conversion_item.idvisit = log_link_visit_action.idvisit AND `log_conversion_item`.deleted = 0';
$this->assertEquals($expected, $generator->getJoinString());
}
private function generate($tables)
{
$generator = $this->makeGenerator($tables);
$generator->generate();
return $generator;
}
private function makeGenerator($tables)
{
if (is_array($tables)) {
$tables = $this->makeTables($tables);
}
return new JoinGenerator($tables);
}
private function makeTables($tables)
{
return new JoinTables(new LogTablesProvider(), $tables);
}
}