Skip to content
39 changes: 39 additions & 0 deletions src/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@

namespace Cycle\Database;

use Cycle\Database\Driver\BulkSchemaProviderInterface;
use Cycle\Database\Driver\Driver;
use Cycle\Database\Driver\DriverInterface;
use Cycle\Database\Driver\CursorInterface;
use Cycle\Database\Driver\CursorOptions;
use Cycle\Database\Exception\DriverException;
use Cycle\Database\Schema\AbstractTable;
use Cycle\Database\Query\DeleteQuery;
use Cycle\Database\Query\InsertQuery;
use Cycle\Database\Query\QueryParameters;
Expand Down Expand Up @@ -117,6 +119,43 @@ public function table(string $name): Table
return new Table($this, $name);
}

/**
* Introspect several tables at once. When the driver supports batched introspection (currently
* Postgres and SQL Server) this costs a constant number of queries instead of a full
* introspection per table; otherwise it falls back to introspecting each table on its own. The
* observable result is identical to calling {@see Table::getSchema()} for each table.
*
* @param non-empty-string[]|null $tables Table names WITHOUT the database prefix. When `null`,
* every table of the database is introspected (names resolved via the driver, then fed
* into the batched path β€” this is the explicit "whole database" entry point).
*
* @return array<non-empty-string, AbstractTable> Keyed by the table name.
*/
public function getSchemas(?array $tables = null): array
{
$handler = $this->getDriver(self::READ)->getSchemaHandler();

if ($tables === null) {
$tables = [];
foreach ($handler->getTableNames($this->prefix) as $table) {
$tables[] = \str_contains($table, '.')
? \str_replace('.' . $this->prefix, '.', $table)
: \substr($table, \strlen($this->prefix));
}
}

if ($handler instanceof BulkSchemaProviderInterface) {
return $handler->getSchemas($tables, $this->prefix);
}

$result = [];
foreach ($tables as $table) {
$result[$table] = $handler->getSchema($table, $this->prefix);
}

return $result;
}

/**
* @psalm-param non-empty-string $query
*/
Expand Down
41 changes: 41 additions & 0 deletions src/Driver/BulkSchemaProviderInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

/**
* This file is part of Cycle ORM package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Cycle\Database\Driver;

use Cycle\Database\Schema\AbstractTable;

/**
* Introspect a set of tables with a constant number of queries instead of one full introspection
* per table.
*
* The list of tables is mandatory: there is deliberately no "no list means the whole database"
* mode, so that the queries never read catalog rows of tables the caller did not ask for. The
* "all tables of the database" scenario is an explicit path in
* {@see \Cycle\Database\Database::getSchemas()} called with no list, which first resolves the names
* via {@see HandlerInterface::getTableNames()} and then passes them here.
*/
interface BulkSchemaProviderInterface
{
/**
* Introspect several tables at once.
*
* Existence is derived from presence in the batched result: a table that does not exist yet is
* returned as an empty schema ({@see AbstractTable::exists()} is `false`), never skipped. For a
* table that does exist the result is identical to {@see HandlerInterface::getSchema()}.
*
* @param non-empty-string[] $tables Table names WITHOUT the database prefix.
* @param string|null $prefix Database specific table prefix applied to every table.
*
* @return array<non-empty-string, AbstractTable> Keyed by the input table name, in the input order.
*/
public function getSchemas(array $tables, ?string $prefix = null): array;
}
22 changes: 21 additions & 1 deletion src/Driver/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
use Cycle\Database\Schema\ComparatorInterface;
use Cycle\Database\Schema\ElementInterface;

abstract class Handler implements HandlerInterface
abstract class Handler implements HandlerInterface, BulkSchemaProviderInterface
{
protected ?DriverInterface $driver = null;

Expand All @@ -34,6 +34,26 @@ public function withDriver(DriverInterface $driver): HandlerInterface
return $handler;
}

/**
* Default implementation introspects each table on its own. Drivers whose catalog can be read
* for a set of tables at once override this with a batched implementation; the observable result
* must stay identical to {@see getSchema()}.
*
* @param non-empty-string[] $tables
*
* @return array<non-empty-string, AbstractTable>
*/
#[\Override]
public function getSchemas(array $tables, ?string $prefix = null): array
{
$result = [];
foreach ($tables as $table) {
$result[$table] = $this->getSchema($table, $prefix);
}

return $result;
}

/**
* Associated driver.
*/
Expand Down
5 changes: 1 addition & 4 deletions src/Driver/HandlerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,22 +61,19 @@ public function withDriver(DriverInterface $driver): self;
/**
* Get all available table names.
*
* @param string|null $prefix
*
* @return array<non-empty-string>
*/
public function getTableNames(string $prefix = ''): array;

/**
* Check if given table exists in database.
*
*/
public function hasTable(string $table): bool;

/**
* Get or create table schema.
*
* @throws HandlerException
*
*/
public function getSchema(string $table, ?string $prefix = null): AbstractTable;

Expand Down
Loading
Loading