Skip to content

Commit

Permalink
Copy connection role to select eager loader
Browse files Browse the repository at this point in the history
  • Loading branch information
Corey Taylor committed Apr 9, 2024
1 parent 0245988 commit 55c2bd3
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/ORM/Association/Loader/SelectLoader.php
Expand Up @@ -178,7 +178,8 @@ protected function _buildQuery(array $options): SelectQuery
->select($options['fields'])
->where($options['conditions'])
->eagerLoaded(true)
->enableHydration($selectQuery->isHydrationEnabled());
->enableHydration($selectQuery->isHydrationEnabled())
->setConnectionRole($selectQuery->getConnectionRole());
if ($selectQuery->isResultsCastingEnabled()) {
$fetchQuery->enableResultsCasting();
} else {
Expand Down
74 changes: 74 additions & 0 deletions tests/TestCase/ORM/Association/BelongsToManyTest.php
Expand Up @@ -18,13 +18,15 @@

use Cake\Database\Connection;
use Cake\Database\Driver;
use Cake\Database\Driver\Sqlite;
use Cake\Database\Exception\DatabaseException;
use Cake\Database\Expression\OrderClauseExpression;
use Cake\Database\Expression\QueryExpression;
use Cake\Datasource\ConnectionManager;
use Cake\Datasource\EntityInterface;
use Cake\Event\EventInterface;
use Cake\I18n\DateTime;
use Cake\Log\Log;
use Cake\ORM\Association\BelongsTo;
use Cake\ORM\Association\BelongsToMany;
use Cake\ORM\Association\HasMany;
Expand Down Expand Up @@ -102,6 +104,13 @@ public function setUp(): void
]);
}

public function tearDown(): void
{
parent::tearDown();
ConnectionManager::drop('test_read_write');
Log::drop('queries');
}

/**
* Tests setForeignKey()
*/
Expand Down Expand Up @@ -1716,4 +1725,69 @@ public function testBindingKeyMatching(): void
// 4 records in the junction table.
$this->assertCount(4, $results);
}

public function testEagerLoaderConnectionRole(): void
{
$this->skipIf(!extension_loaded('pdo_sqlite'), 'Skipping as SQLite extension is missing');

Log::setConfig('queries', [
'className' => 'Array',
'scopes' => ['queriesLog'],
]);

ConnectionManager::setConfig('test_read_write', [
'className' => Connection::class,
'driver' => Sqlite::class,
'write' => [
'database' => ':memory:',
'cached' => 'shared', // used so role configs are unique
'log' => true,
],
'read' => [
'database' => ':memory:',
'log' => true,
],
]);

$connection = ConnectionManager::get('test_read_write');
$this->assertNotSame($connection->getDriver(Connection::ROLE_READ), $connection->getDriver(Connection::ROLE_WRITE));

// Create belongs to many relationships with unique table names
$driver = $connection->getDriver(Connection::ROLE_WRITE);
$driver->execute('CREATE TABLE unique_items (id int PRIMARY KEY);');
$driver->execute('CREATE TABLE articles (id int PRIMARY KEY);');
$driver->execute('CREATE TABLE articles_unique_items (unique_item_id int, article_id int);');

$driver = $connection->getDriver(Connection::ROLE_READ);
$driver->execute('CREATE TABLE unique_items (id int PRIMARY KEY);');
$driver->execute('CREATE TABLE articles (id int PRIMARY KEY);');
$driver->execute('CREATE TABLE articles_unique_items (unique_item_id int, article_id int);');
$driver->execute('INSERT INTO unique_items (id) VALUES (1)');
$driver->execute('INSERT INTO articles (id) VALUES (1)');
$driver->execute('INSERT INTO articles_unique_items (unique_item_id, article_id) VALUES (1, 1)');

$articles = $this->getTableLocator()->get('Articles')->setConnection($connection);
$articles->belongsToMany('UniqueItems')->getTarget()->setConnection($connection);

$query = $articles->find();
$this->assertSame(Connection::ROLE_WRITE, $query->getConnectionRole(), 'This test assumes select queries still default to write role');

$results = $query->contain('UniqueItems')->useReadRole()->toArray();
$this->assertCount(1, $results);
$this->assertCount(1, $results[0]->unique_items);
$this->assertSame(1, $results[0]->unique_items[0]->id);

$logs = Log::engine('queries')->read();
$this->assertNotEmpty($logs);

foreach ($logs as $log) {
if (
str_contains($log, 'FROM articles') ||
str_contains($log, 'FROM articles_unique_items') ||
str_contains($log, 'FROM unique_items')
) {
$this->assertStringContainsString('role=read', $log);
}
}
}
}
71 changes: 71 additions & 0 deletions tests/TestCase/ORM/Association/HasManyTest.php
Expand Up @@ -16,13 +16,16 @@
*/
namespace Cake\Test\TestCase\ORM\Association;

use Cake\Database\Connection;
use Cake\Database\Driver\Sqlite;
use Cake\Database\Driver\Sqlserver;
use Cake\Database\Expression\OrderByExpression;
use Cake\Database\Expression\OrderClauseExpression;
use Cake\Database\Expression\QueryExpression;
use Cake\Database\Expression\TupleComparison;
use Cake\Database\TypeMap;
use Cake\Datasource\ConnectionManager;
use Cake\Log\Log;
use Cake\ORM\Association;
use Cake\ORM\Association\HasMany;
use Cake\ORM\Entity;
Expand Down Expand Up @@ -116,6 +119,13 @@ public function setUp(): void
$this->autoQuote = $connection->getDriver()->isAutoQuotingEnabled();
}

public function tearDown(): void
{
parent::tearDown();
ConnectionManager::drop('test_read_write');
Log::drop('queries');
}

/**
* Tests that foreignKey() returns the correct configured value
*/
Expand Down Expand Up @@ -1585,4 +1595,65 @@ public function testHasManyNonDependentNonCascadingUnlinkUpdateUsesAssociationCo
$this->assertNull($Articles->get($article2->get('id'))->get('author_id'));
$this->assertEquals($author->get('id'), $Articles->get($article3->get('id'))->get('author_id'));
}

public function testEagerLoaderConnectionRole(): void
{
$this->skipIf(!extension_loaded('pdo_sqlite'), 'Skipping as SQLite extension is missing');

Log::setConfig('queries', [
'className' => 'Array',
'scopes' => ['queriesLog'],
]);

ConnectionManager::setConfig('test_read_write', [
'className' => Connection::class,
'driver' => Sqlite::class,
'write' => [
'database' => ':memory:',
'cached' => 'shared', // used so role configs are unique
'log' => true,
],
'read' => [
'database' => ':memory:',
'log' => true,
],
]);

$connection = ConnectionManager::get('test_read_write');
$this->assertNotSame($connection->getDriver(Connection::ROLE_READ), $connection->getDriver(Connection::ROLE_WRITE));

// Create belongs to many relationships with unique table names
$driver = $connection->getDriver(Connection::ROLE_WRITE);
$driver->execute('CREATE TABLE unique_items (id int PRIMARY KEY, article_id int);');
$driver->execute('CREATE TABLE articles (id int PRIMARY KEY);');

$driver = $connection->getDriver(Connection::ROLE_READ);
$driver->execute('CREATE TABLE unique_items (id int PRIMARY KEY, article_id int);');
$driver->execute('CREATE TABLE articles (id int PRIMARY KEY);');
$driver->execute('INSERT INTO unique_items (id, article_id) VALUES (1, 1)');
$driver->execute('INSERT INTO articles (id) VALUES (1)');

$articles = $this->getTableLocator()->get('Articles')->setConnection($connection);
$articles->hasMany('UniqueItems')->setStrategy('select')->getTarget()->setConnection($connection);

$query = $articles->find();
$this->assertSame(Connection::ROLE_WRITE, $query->getConnectionRole(), 'This test assumes select queries still default to write role');

$results = $query->contain('UniqueItems')->useReadRole()->toArray();
$this->assertCount(1, $results);
$this->assertCount(1, $results[0]->unique_items);
$this->assertSame(1, $results[0]->unique_items[0]->id);

$logs = Log::engine('queries')->read();
$this->assertNotEmpty($logs);

foreach ($logs as $log) {
if (
str_contains($log, 'FROM articles') ||
str_contains($log, 'FROM unique_items')
) {
$this->assertStringContainsString('role=read', $log);
}
}
}
}

0 comments on commit 55c2bd3

Please sign in to comment.