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

IBX-8566: Fixed postgres language limit #454

Merged
merged 9 commits into from
Nov 25, 2024
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
11 changes: 1 addition & 10 deletions src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php
Original file line number Diff line number Diff line change
Expand Up @@ -1357,16 +1357,7 @@ public function setName(int $contentId, int $version, string $name, string $lang
*/
private function getSetNameLanguageMaskSubQuery(): string
{
return <<<SQL
(SELECT
CASE
WHEN (initial_language_id = :language_id AND (language_mask & :language_id) <> 0 )
THEN (:language_id | 1)
ELSE :language_id
END
FROM ezcontentobject
WHERE id = :content_id)
SQL;
return $this->sharedGateway->getSetNameLanguageMaskSubQuery();
}

public function deleteContent(int $contentId): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ public function extractLanguageIdsFromMask($languageMask): array
$result = [];

// Decomposition of $languageMask into its binary components.
while ($exp <= $languageMask) {
// check if $exp has not overflown and became float (happens for the last possible language in the mask)
while (is_int($exp) && $exp <= $languageMask) {
Steveb-p marked this conversation as resolved.
Show resolved Hide resolved
if ($languageMask & $exp) {
$result[] = $exp;
}
Expand Down
3 changes: 2 additions & 1 deletion src/lib/Persistence/Legacy/Content/Mapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,8 @@ private function extractLanguageCodesFromMask(int $languageMask, array $allLangu
$result = [];

// Decomposition of $languageMask into its binary components to extract language codes
while ($exp <= $languageMask) {
// check if $exp has not overflown and became float (happens for the last possible language in the mask)
while (is_int($exp) && $exp <= $languageMask) {
if ($languageMask & $exp) {
if (isset($allLanguages[$exp])) {
$result[] = $allLanguages[$exp]->languageCode;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Core\Persistence\Legacy\SharedGateway\DatabasePlatform;

use Doctrine\DBAL\Connection;
use Ibexa\Core\Persistence\Legacy\SharedGateway\Gateway;

/**
* @internal
*/
abstract class AbstractGateway implements Gateway
{
protected Connection $connection;

public function __construct(Connection $connection)
{
$this->connection = $connection;
}

public function getColumnNextIntegerValue(
string $tableName,
string $columnName,
string $sequenceName
): ?int {
return null;
}

/**
* Return a language sub select query for setName.
*
* The query generates the proper language mask at the runtime of the INSERT/UPDATE query
* generated by setName.
*
* @see setName
*/
public function getSetNameLanguageMaskSubQuery(): string
{
return <<<SQL
(SELECT
CASE
WHEN (initial_language_id = :language_id AND (language_mask & :language_id) <> 0 )
THEN (:language_id | 1)
ELSE :language_id
END
FROM ezcontentobject
WHERE id = :content_id)
Steveb-p marked this conversation as resolved.
Show resolved Hide resolved
SQL;
}

public function getLastInsertedId(string $sequenceName): int
{
return (int)$this->connection->lastInsertId($sequenceName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,8 @@

namespace Ibexa\Core\Persistence\Legacy\SharedGateway\DatabasePlatform;

use Doctrine\DBAL\Connection;
use Ibexa\Core\Persistence\Legacy\SharedGateway\Gateway;

final class FallbackGateway implements Gateway
final class FallbackGateway extends AbstractGateway
{
/** @var \Doctrine\DBAL\Connection */
private $connection;

public function __construct(Connection $connection)
{
$this->connection = $connection;
}

public function getColumnNextIntegerValue(
string $tableName,
string $columnName,
string $sequenceName
): ?int {
return null;
}

public function getLastInsertedId(string $sequenceName): int
{
return (int)$this->connection->lastInsertId($sequenceName);
}
}

class_alias(FallbackGateway::class, 'eZ\Publish\Core\Persistence\Legacy\SharedGateway\DatabasePlatform\FallbackGateway');
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Core\Persistence\Legacy\SharedGateway\DatabasePlatform;

final class PostgresqlGateway extends AbstractGateway
{
/**
* Return a language sub select query for setName.
*
* The query generates the proper language mask at the runtime of the INSERT/UPDATE query
* generated by setName.
*
* @see setName
*/
public function getSetNameLanguageMaskSubQuery(): string
{
return <<<SQL
(SELECT
CASE
WHEN (initial_language_id = :language_id AND (language_mask & :language_id) <> 0 )
THEN (cast(:language_id as BIGINT) | 1)
ELSE :language_id
END
FROM ezcontentobject
WHERE id = :content_id)
SQL;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@
namespace Ibexa\Core\Persistence\Legacy\SharedGateway\DatabasePlatform;

use Ibexa\Core\Base\Exceptions\DatabaseException;
use Ibexa\Core\Persistence\Legacy\SharedGateway\Gateway;

final class SqliteGateway implements Gateway
final class SqliteGateway extends AbstractGateway
{
/**
* Error code 7 for a fatal error - taken from an existing driver implementation.
Expand All @@ -21,7 +20,7 @@ final class SqliteGateway implements Gateway
private const DB_INT_MAX = 2147483647;

/** @var array<string, int> */
private $lastInsertedIds = [];
private array $lastInsertedIds = [];

public function getColumnNextIntegerValue(
string $tableName,
Expand Down
5 changes: 5 additions & 0 deletions src/lib/Persistence/Legacy/SharedGateway/Gateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ public function getColumnNextIntegerValue(
* It returns integer as all the IDs in the Ibexa Legacy Storage are (big)integers
*/
public function getLastInsertedId(string $sequenceName): int;

/**
* Return a language sub select query for setName.
*/
public function getSetNameLanguageMaskSubQuery(): string;
}

class_alias(Gateway::class, 'eZ\Publish\Core\Persistence\Legacy\SharedGateway\Gateway');
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@ services:
bind:
$connection: '@ibexa.persistence.connection'

Ibexa\Core\Persistence\Legacy\SharedGateway\DatabasePlatform\AbstractGateway: ~

Ibexa\Core\Persistence\Legacy\SharedGateway\DatabasePlatform\FallbackGateway: ~

Ibexa\Core\Persistence\Legacy\SharedGateway\DatabasePlatform\SqliteGateway:
tags:
- { name: ibexa.storage.legacy.gateway.shared, platform: sqlite }

Ibexa\Core\Persistence\Legacy\SharedGateway\DatabasePlatform\PostgresqlGateway:
tags:
- { name: ibexa.storage.legacy.gateway.shared, platform: postgresql }

Ibexa\Core\Persistence\Legacy\SharedGateway\GatewayFactory:
arguments:
$fallbackGateway: '@Ibexa\Core\Persistence\Legacy\SharedGateway\DatabasePlatform\FallbackGateway'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Tests\Integration\Core\Repository\ContentService;

use Ibexa\Tests\Integration\Core\RepositoryTestCase;
use Symfony\Component\Yaml\Yaml;

/**
* @covers \Ibexa\Contracts\Core\Repository\ContentService
*/
final class MaxLanguagesContentServiceTest extends RepositoryTestCase
{
/** @var list<array{languageCode: string, name: string }> */
private static array $languagesRawList = [];

public static function setUpBeforeClass(): void
{
parent::setUpBeforeClass();

self::$languagesRawList = Yaml::parseFile(dirname(__DIR__) . '/_fixtures/max_languages.yaml');
}
reithor marked this conversation as resolved.
Show resolved Hide resolved

/**
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException
*/
protected function setUp(): void
{
parent::setUp();

$this->prepareMaxLanguages();
}

/**
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception
*/
public function testCreateContent(): void
{
$names = array_merge(...array_map(
static fn (array $languageData): array => [
$languageData['languageCode'] => $languageData['name'] . ' name',
],
self::$languagesRawList
));
$this->createFolder($names);
}

/**
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException
*/
private function prepareMaxLanguages(): void
{
$languageService = self::getLanguageService();

foreach (self::$languagesRawList as $languageData) {
$languageCreateStruct = $languageService->newLanguageCreateStruct();
$languageCreateStruct->languageCode = $languageData['languageCode'];
$languageCreateStruct->name = $languageData['name'];
$languageService->createLanguage($languageCreateStruct);
}
}
}
62 changes: 62 additions & 0 deletions tests/integration/Core/Repository/_fixtures/max_languages.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
- { languageCode: alb-SQ, name: Albanian }
- { languageCode: ara-AR, name: Arabic }
- { languageCode: aze-AZ, name: Azerbaijani }
- { languageCode: bos-BS, name: Bosnian }
- { languageCode: cha-CH, name: Chamorro }
- { languageCode: chi-ZH, name: Chinese }
- { languageCode: cze-CS, name: Czech }
- { languageCode: dan-DA, name: Danish }
- { languageCode: dut-NL, name: Dutch (Flemish) }
#- { languageCode: eng-GB, name: English (United Kingdom) } # Pre-exists in the initial db fixture
#- { languageCode: eng-US, name: English (United States) } # Pre-exists in the initial db fixture
- { languageCode: eng-AU, name: English (Australia) }
- { languageCode: epo-EO, name: Esperanto }
- { languageCode: esp-ES, name: Spanish }
- { languageCode: esp-MX, name: Spanish (Mexico) }
- { languageCode: est-ET, name: Estonian }
- { languageCode: fas-FA, name: Persian (Farsi) }
- { languageCode: fin-FI, name: Finnish }
- { languageCode: fre-FR, name: French (France) }
- { languageCode: fre-BE, name: French (Belgium) }
- { languageCode: fre-CA, name: French (Canada) }
- { languageCode: fre-CH, name: French (Switzerland) }
- { languageCode: fre-LU, name: French (Luxembourg) }
- { languageCode: geo-KA, name: Georgian }
#- { languageCode: ger-DE, name: German (Germany) } # Pre-exists in the initial db fixture
- { languageCode: ger-AT, name: German (Austria) }
- { languageCode: ger-CH, name: German (Switzerland) }
- { languageCode: ger-LI, name: German (Liechtenstein) }
- { languageCode: ger-LU, name: German (Luxembourg) }
- { languageCode: gle-GA, name: Irish }
- { languageCode: gla-GD, name: Scottish (Gaelic) }
- { languageCode: gre-EL, name: Greek }
- { languageCode: hin-HI, name: Hebrew }
- { languageCode: heb-HE, name: Hebrew }
- { languageCode: hrv-HR, name: Croatian }
- { languageCode: hun-HU, name: Hungarian }
- { languageCode: ind-ID, name: Indonesian }
- { languageCode: isl-IS, name: Icelandic }
- { languageCode: ita-IT, name: Italian }
- { languageCode: jpn-JA, name: Japanese }
- { languageCode: kor-KO, name: Korean }
- { languageCode: lat-LA, name: Latin }
- { languageCode: lav-LV, name: Latvian }
- { languageCode: lit-LT, name: Lithuanian }
- { languageCode: mao-MI, name: Maori (New Zealand) }
- { languageCode: may-MS, name: Malay }
- { languageCode: nor-NO, name: Norwegian }
- { languageCode: pol-PL, name: Polish }
- { languageCode: por-PT, name: Portuguese (Portugal) }
- { languageCode: por-BR, name: Portuguese (Brazil) }
- { languageCode: rum-RO, name: Romanian }
- { languageCode: slo-SK, name: Slovak }
- { languageCode: swe-SV, name: Swedish }
- { languageCode: bul-BG, name: Bulgarian }
- { languageCode: swa-SW, name: Swahili (Swahili) }
- { languageCode: tha-TH, name: Thai }
- { languageCode: tib-BO, name: Tibetan }
- { languageCode: tlh-TL, name: Klingon }
- { languageCode: tur-TR, name: Turkish }
- { languageCode: ukr-UK, name: Ukrainian }
- { languageCode: wel-CY, name: Welsh (Swahili) }
- { languageCode: yid-YI, name: Yiddish }
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ final class GatewayFactoryTest extends TestCase
public function setUp(): void
{
$gateways = [
'sqlite' => new SqliteGateway(),
'sqlite' => new SqliteGateway($this->createMock(Connection::class)),
];

$this->factory = new GatewayFactory(
Expand Down
2 changes: 1 addition & 1 deletion tests/lib/Persistence/Legacy/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ final public function getSharedGateway(): SharedGateway\Gateway
$factory = new SharedGateway\GatewayFactory(
new SharedGateway\DatabasePlatform\FallbackGateway($connection),
[
'sqlite' => new SharedGateway\DatabasePlatform\SqliteGateway(),
'sqlite' => new SharedGateway\DatabasePlatform\SqliteGateway($connection),
]
);

Expand Down
Loading