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

Add schema/attach support for Sqlite #1033

Draft
wants to merge 7 commits into
base: develop
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

$finder = PhpCsFixer\Finder::create()
->in([__DIR__])
->exclude(['vendor']);
->exclude(['vendor', 'src/Persistence/Sql/Sqlite/debug-attach']);

return (new PhpCsFixer\Config())
->setRiskyAllowed(true)
Expand Down
3 changes: 3 additions & 0 deletions bootstrap-types.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
use Doctrine\DBAL\Schema\SqliteSchemaManager;
use Doctrine\DBAL\Types as DbalTypes;

require_once __DIR__ . '/src/Persistence/Sql/Sqlite/debug-attach/SqliteSchemaManager.php';
require_once __DIR__ . '/src/Persistence/Sql/Sqlite/debug-attach/SqlitePlatform.php';

// force SQLitePlatform and SQLiteSchemaManager classes load as in DBAL 3.x they are named with a different case
// remove once DBAL 3.x support is dropped
new \ReflectionClass(SqlitePlatform::class);
Expand Down
1 change: 1 addition & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ parameters:
- .
excludePaths:
- vendor
- src/Persistence/Sql/Sqlite/debug-attach

checkMissingOverrideMethodAttribute: true

Expand Down
47 changes: 47 additions & 0 deletions src/Persistence/Sql/Sqlite/PlatformTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@

trait PlatformTrait
{
use PlatformTraitBackport5517 {
PlatformTraitBackport5517::getListTableConstraintsSQL as private __getListTableConstraintsSQL;
PlatformTraitBackport5517::getListTableColumnsSQL as private __getListTableColumnsSQL;
PlatformTraitBackport5517::getListTableIndexesSQL as private __getListTableIndexesSQL;
PlatformTraitBackport5517::getListTableForeignKeysSQL as private __getListTableForeignKeysSQL;
}

public function __construct()
{
$this->disableSchemaEmulation(); // @phpstan-ignore-line
Expand All @@ -20,6 +27,46 @@ public function getIdentifierQuoteCharacter(): string
return '`';
}

public function supportsForeignKeyConstraints(): bool
{
// backport https://github.com/doctrine/dbal/pull/5427, remove once DBAL 3.3.x support is dropped
return true;
}

protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff): array
{
// https://github.com/doctrine/dbal/pull/5486
return [];
}

// fix quoted table name support
// TODO submit a PR with fixed SQLitePlatform to DBAL

private function unquoteTableIdentifier(string $tableName): string
{
return (new Identifier($tableName))->getName();
}

public function getListTableConstraintsSQL($table)
{
return $this->__getListTableConstraintsSQL($this->unquoteTableIdentifier($table));
}

public function getListTableColumnsSQL($table, $database = null)
{
return $this->__getListTableColumnsSQL($this->unquoteTableIdentifier($table), $database);
}

public function getListTableIndexesSQL($table, $database = null)
{
return $this->__getListTableIndexesSQL($this->unquoteTableIdentifier($table), $database);
}

public function getListTableForeignKeysSQL($table, $database = null)
{
return $this->__getListTableForeignKeysSQL($this->unquoteTableIdentifier($table), $database);
}

#[\Override]
public function getAlterTableSQL(TableDiff $diff): array
{
Expand Down
123 changes: 123 additions & 0 deletions src/Persistence/Sql/Sqlite/PlatformTraitBackport5517.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php

declare(strict_types=1);

namespace Atk4\Data\Persistence\Sql\Sqlite;

use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\Identifier;

/**
* Drop once https://github.com/doctrine/dbal/pull/5517 is merged and DBAL 3.3.x support is removed.
*/
trait PlatformTraitBackport5517
{
private function emulateSchemaNamespacing(string $tableName): string
{
return $tableName;
}

public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey)
{
return parent::getForeignKeyDeclarationSQL(new ForeignKeyConstraint(
$foreignKey->getQuotedLocalColumns($this),
$this->emulateSchemaNamespacing($foreignKey->getQuotedForeignTableName($this)),
$foreignKey->getQuotedForeignColumns($this),
$foreignKey->getName(),
$foreignKey->getOptions()
));
}

protected function _getCreateTableSQL($name, array $columns, array $options = [])
{
$name = $this->emulateSchemaNamespacing($name);
$queryFields = $this->getColumnDeclarationListSQL($columns);

if (isset($options['uniqueConstraints']) && !empty($options['uniqueConstraints'])) {
foreach ($options['uniqueConstraints'] as $constraintName => $definition) {
$queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($constraintName, $definition);
}
}

$queryFields .= \Closure::bind(fn () => $this->getNonAutoincrementPrimaryKeyDefinition($columns, $options), $this, parent::class)();

if (isset($options['foreignKeys'])) {
foreach ($options['foreignKeys'] as $foreignKey) {
$queryFields .= ', ' . $this->getForeignKeyDeclarationSQL($foreignKey);
}
}

$tableComment = '';
if (isset($options['comment'])) {
$comment = trim($options['comment'], " '");

$tableComment = \Closure::bind(fn () => $this->getInlineTableCommentSQL($comment), $this, parent::class)();
}

$query = ['CREATE TABLE ' . $name . ' ' . $tableComment . '(' . $queryFields . ')'];

if (isset($options['alter']) && $options['alter'] === true) {
return $query;
}

if (isset($options['indexes']) && !empty($options['indexes'])) {
foreach ($options['indexes'] as $indexDef) {
$query[] = $this->getCreateIndexSQL($indexDef, $name);
}
}

if (isset($options['unique']) && !empty($options['unique'])) {
foreach ($options['unique'] as $indexDef) {
$query[] = $this->getCreateIndexSQL($indexDef, $name);
}
}

return $query;
}

public function getListTableConstraintsSQL($table)
{
$table = $this->emulateSchemaNamespacing($table);

return sprintf(
"SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name = %s AND sql NOT NULL ORDER BY name",
$this->quoteStringLiteral($table)
);
}

public function getListTableColumnsSQL($table, $database = null)
{
$table = $this->emulateSchemaNamespacing($table);

return sprintf('PRAGMA table_info(%s)', $this->quoteStringLiteral($table));
}

public function getListTableIndexesSQL($table, $database = null)
{
$table = $this->emulateSchemaNamespacing($table);

return sprintf('PRAGMA index_list(%s)', $this->quoteStringLiteral($table));
}

public function getTruncateTableSQL($tableName, $cascade = false)
{
$tableIdentifier = new Identifier($tableName);
$tableName = $this->emulateSchemaNamespacing($tableIdentifier->getQuotedName($this));

return 'DELETE FROM ' . $tableName;
}

public function getTemporaryTableName($tableName)
{
$tableName = $this->emulateSchemaNamespacing($tableName);

return $tableName;
}

public function getListTableForeignKeysSQL($table, $database = null)
{
$table = $this->emulateSchemaNamespacing($table);

return sprintf('PRAGMA foreign_key_list(%s)', $this->quoteStringLiteral($table));
}
}
Loading
Loading