Skip to content

Commit

Permalink
Merge branch '3.7.x' into 4.0.x
Browse files Browse the repository at this point in the history
* 3.7.x:
  Avoid self deprecation about listTableColumn (doctrine#6108)
  Fix blob binding overwrite on DB2 (doctrine#6093)
  Optimize TypeRegistry::lookupName() from O(N) to O(1) (doctrine#6082)
  Add documentation for doctrine#6044 (doctrine#6069)
  Use triggerIfCalledFromOutside in listTableDetails
  Document alternative to Type::getName (doctrine#6077)
  • Loading branch information
derrabus committed Jul 24, 2023
2 parents 54ae67d + 982b474 commit c97a3e6
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 13 deletions.
4 changes: 3 additions & 1 deletion UPGRADE.md
Expand Up @@ -1567,11 +1567,13 @@ This method is unused by the DBAL since 2.0.

## Deprecated `Type::getName()`

This will method is not useful for the DBAL anymore, and will be removed in 4.0.
This method is not useful for the DBAL anymore, and will be removed in 4.0.
As a consequence, depending on the name of a type being `json` for `jsonb` to
be used for the Postgres platform is deprecated in favor of extending
`Doctrine\DBAL\Types\JsonType`.

You can use `Type::getTypeRegistry()->lookupName($type)` instead.

## Deprecated `AbstractPlatform::getColumnComment()`, `AbstractPlatform::getDoctrineTypeComment()`,
`AbstractPlatform::hasNative*Type()` and `Type::requiresSQLCommentHint()`

Expand Down
19 changes: 19 additions & 0 deletions docs/en/reference/schema-representation.rst
Expand Up @@ -76,6 +76,25 @@ All schema assets reside in the ``Doctrine\DBAL\Schema`` namespace.

This chapter is far from being completely documented.

Table
~~~~~~

Represents a table in the schema.

Vendor specific options
^^^^^^^^^^^^^^^^^^^^^^^

The following options, that can be set using ``default_table_options``, are completely vendor specific
and absolutely not portable.

- **charset** (string): The character set to use for the table. Currently only supported
on MySQL.

- **engine** (string): The DB engine used for the table. Currently only supported on MySQL.

- **unlogged** (boolean): Set a PostgreSQL table type as
`unlogged <https://www.postgresql.org/docs/current/sql-createtable.htmll>`_

Column
~~~~~~

Expand Down
2 changes: 2 additions & 0 deletions src/Driver/IBMDB2/Statement.php
Expand Up @@ -118,6 +118,8 @@ private function bindLobs(): array
} else {
$this->bind($param, $value, DB2_PARAM_IN, DB2_CHAR);
}

unset($value);
}

return $handles;
Expand Down
43 changes: 31 additions & 12 deletions src/Types/TypeRegistry.php
Expand Up @@ -11,17 +11,26 @@
use Doctrine\DBAL\Types\Exception\TypesAlreadyExists;
use Doctrine\DBAL\Types\Exception\UnknownColumnType;

use function array_search;
use function in_array;
use function spl_object_id;

/**
* The type registry is responsible for holding a map of all known DBAL types.
*/
final class TypeRegistry
{
/** @var array<string, Type> Map of type names and their corresponding flyweight objects. */
private array $instances;
/** @var array<int, string> */
private array $instancesReverseIndex;

/** @param array<string, Type> $instances */
public function __construct(private array $instances = [])
public function __construct(array $instances = [])
{
$this->instances = [];
$this->instancesReverseIndex = [];
foreach ($instances as $name => $type) {
$this->register($name, $type);
}
}

/**
Expand All @@ -31,11 +40,12 @@ public function __construct(private array $instances = [])
*/
public function get(string $name): Type
{
if (! isset($this->instances[$name])) {
$type = $this->instances[$name] ?? null;
if ($type === null) {
throw UnknownColumnType::new($name);
}

return $this->instances[$name];
return $type;
}

/**
Expand All @@ -45,9 +55,9 @@ public function get(string $name): Type
*/
public function lookupName(Type $type): string
{
$name = array_search($type, $this->instances, true);
$name = $this->findTypeName($type);

if ($name === false) {
if ($name === null) {
throw TypeNotRegistered::new($type);
}

Expand All @@ -73,11 +83,12 @@ public function register(string $name, Type $type): void
throw TypesAlreadyExists::new($name);
}

if (array_search($type, $this->instances, true) !== false) {
if ($this->findTypeName($type) !== null) {
throw TypeAlreadyRegistered::new($type);
}

$this->instances[$name] = $type;
$this->instances[$name] = $type;
$this->instancesReverseIndex[spl_object_id($type)] = $name;
}

/**
Expand All @@ -87,15 +98,18 @@ public function register(string $name, Type $type): void
*/
public function override(string $name, Type $type): void
{
if (! isset($this->instances[$name])) {
$origType = $this->instances[$name] ?? null;
if ($origType === null) {
throw TypeNotFound::new($name);
}

if (! in_array(array_search($type, $this->instances, true), [$name, false], true)) {
if (($this->findTypeName($type) ?? $name) !== $name) {
throw TypeAlreadyRegistered::new($type);
}

$this->instances[$name] = $type;
unset($this->instancesReverseIndex[spl_object_id($origType)]);
$this->instances[$name] = $type;
$this->instancesReverseIndex[spl_object_id($type)] = $name;
}

/**
Expand All @@ -109,4 +123,9 @@ public function getMap(): array
{
return $this->instances;
}

private function findTypeName(Type $type): ?string
{
return $this->instancesReverseIndex[spl_object_id($type)] ?? null;
}
}
28 changes: 28 additions & 0 deletions tests/Functional/BlobTest.php
Expand Up @@ -172,6 +172,34 @@ public function testBindParamProcessesStream(): void
$this->assertBlobContains('test');
}

public function testBlobBindingDoesNotOverwritePrevious(): void
{
$table = new Table('blob_table');
$table->addColumn('id', 'integer');
$table->addColumn('blobcolumn1', 'blob', ['notnull' => false]);
$table->addColumn('blobcolumn2', 'blob', ['notnull' => false]);
$table->setPrimaryKey(['id']);
$this->dropAndCreateTable($table);

$params = ['test1', 'test2'];
$this->connection->executeStatement(
'INSERT INTO blob_table(id, blobcolumn1, blobcolumn2) VALUES (1, ?, ?)',
$params,
[ParameterType::LARGE_OBJECT, ParameterType::LARGE_OBJECT],
);

$blobs = $this->connection->fetchNumeric('SELECT blobcolumn1, blobcolumn2 FROM blob_table');
self::assertIsArray($blobs);

$actual = [];
foreach ($blobs as $blob) {
$blob = Type::getType('blob')->convertToPHPValue($blob, $this->connection->getDatabasePlatform());
$actual[] = stream_get_contents($blob);
}

self::assertEquals(['test1', 'test2'], $actual);
}

private function assertBlobContains(string $text): void
{
[, $blobValue] = $this->fetchRow();
Expand Down
8 changes: 8 additions & 0 deletions tests/Types/TypeRegistryTest.php
Expand Up @@ -99,6 +99,14 @@ public function testRegisterWithAlreadyRegisteredInstance(): void
$this->registry->register('type2', $newType);
}

public function testConstructorWithDuplicateInstance(): void
{
$newType = new TextType();

$this->expectException(Exception::class);
new TypeRegistry(['a' => $newType, 'b' => $newType]);
}

public function testOverride(): void
{
$baseType = new TextType();
Expand Down

0 comments on commit c97a3e6

Please sign in to comment.