diff --git a/composer.json b/composer.json index 8f9ad1fed0f34398c6b46cb296c68af5d6f32e69..6f1db68eae2a7c344ce34c6e885d8fe68f78f248 100644 --- a/composer.json +++ b/composer.json @@ -1,22 +1,22 @@ { - "name" : "unicaen/bddadmin", - "description" : "Module pour administrer des bases de données Postgresql et Oracle", - "repositories": [ - { - "type": "composer", - "url" : "https://dev.unicaen.fr/packagist" - } - ], - "require": { - "php": ">=8.2" - }, - "autoload" : { - "psr-4" : { - "Unicaen\\BddAdmin\\" : "src/", - "Unicaen\\BddAdmin\\Tests\\" : "tests/" - }, - "classmap": [ - "./Module.php" - ] + "name": "unicaen/bddadmin", + "description": "Module pour administrer des bases de données Postgresql et Oracle", + "repositories": [ + { + "type": "composer", + "url": "https://dev.unicaen.fr/packagist" } + ], + "require": { + "php": ">=8.2" + }, + "autoload": { + "psr-4": { + "Unicaen\\BddAdmin\\": "src/", + "Unicaen\\BddAdmin\\Tests\\": "tests/" + }, + "classmap": [ + "./Module.php" + ] + } } diff --git a/config/module.config.php b/config/module.config.php index 7d4bc094a599921fa5d83ac2f0b6703c3f51acb8..bb7f7380ed94e5d3d05b031256053c394672fb6f 100644 --- a/config/module.config.php +++ b/config/module.config.php @@ -54,7 +54,17 @@ return [ 'service_manager' => [ 'factories' => [ - Bdd::class => BddFactory::class, + Bdd::class => BddFactory::class, + Command\UpdateBddCommand::class => Command\UpdateBddCommandFactory::class, + Command\UpdateDdlCommand::class => Command\UpdateDdlCommandFactory::class, + ], + ], + + + 'laminas-cli' => [ + 'commands' => [ + 'bddadmin:update-bdd' => Command\UpdateBddCommand::class, + 'bddadmin:update-ddl' => Command\UpdateDdlCommand::class, ], ], ]; \ No newline at end of file diff --git a/src/Command/UpdateBddCommand.php b/src/Command/UpdateBddCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..7e75b266096f80a9e0c91d853aa1dfcdc5881e5b --- /dev/null +++ b/src/Command/UpdateBddCommand.php @@ -0,0 +1,80 @@ +<?php + +namespace Unicaen\BddAdmin\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Unicaen\BddAdmin\BddAwareTrait; +use Unicaen\BddAdmin\Ddl\Ddl; + +/** + * Description of UpdateBddCommand + * + * @author Laurent Lécluse <laurent.lecluse at unicaen.fr> + */ +class UpdateBddCommand extends Command +{ + use BddAwareTrait; + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $bdd = $this->getBdd()->setLogger($io); + + $io->title('Mise à jour de la base de données'); + $io->info('Mise à jour des définitions de la base de données. Merci de patienter ...'); + + $ref = $bdd->getRefDdl(); + + // Construction de la config de DDL pour filtrer + $filters = []; + foreach ($ref as $ddlClass => $objects) { + foreach ($objects as $object => $objectDdl) { + $filters[$ddlClass]['includes'][] = $object; + } + } + + $tablesDep = [ + Ddl::INDEX, + Ddl::PRIMARY_CONSTRAINT, + Ddl::REF_CONSTRAINT, + Ddl::UNIQUE_CONSTRAINT, + ]; + + foreach ($tablesDep as $tableDep) { + $objects = $bdd->manager($tableDep)->get(); + foreach ($objects as $obj) { + if (in_array($obj['table'], $filters['table']['includes'])) { + $filters[$tableDep]['includes'][] = $obj['name']; + } + } + } + + // Initialisation et lancement de la pré-migration + //$mm = new MigrationManager($oa, $ref, $filters); + //$mm->migration('before'); + + + // Mise à jour de la BDD (structures) + $bdd->alter($ref, $filters, true); + + + // Mise à jour des séquences + $bdd->majSequences($ref); + + + // Mise à jour des données + //$bdd->logBegin('Contrôle et mise à jour des données'); + //$bdd->dataUpdater()->run('update'); + //$bdd->logEnd('Données à jour'); + + + + // Post-migration + //$mm->migration('after'); + + return Command::SUCCESS; + } +} \ No newline at end of file diff --git a/src/Command/UpdateBddCommandFactory.php b/src/Command/UpdateBddCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..4b5ef7dc3dd9cb67cb2caa07fd7f986bbe8da14a --- /dev/null +++ b/src/Command/UpdateBddCommandFactory.php @@ -0,0 +1,31 @@ +<?php + +namespace Unicaen\BddAdmin\Command; + +use Psr\Container\ContainerInterface; +use Unicaen\BddAdmin\Bdd; + + +/** + * Description of UpdateBddCommandFactory + * + * @author Laurent Lécluse <laurent.lecluse at unicaen.fr> + */ +class UpdateBddCommandFactory +{ + + /** + * @param ContainerInterface $container + * @param string $requestedName + * @param array|null $options + * + * @return UpdateBddCommand + */ + public function __invoke(ContainerInterface $container, $requestedName, $options = null): UpdateBddCommand + { + $command = new UpdateBddCommand; + $command->setBdd($container->get(Bdd::class)); + + return $command; + } +} \ No newline at end of file diff --git a/src/Command/UpdateDdlCommand.php b/src/Command/UpdateDdlCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..9fdeecf80a7b4b84a8a23afc6912ca72d9329c09 --- /dev/null +++ b/src/Command/UpdateDdlCommand.php @@ -0,0 +1,36 @@ +<?php + +namespace Unicaen\BddAdmin\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Unicaen\BddAdmin\BddAwareTrait; + +/** + * Description of UpdateDdlCommand + * + * @author Laurent Lécluse <laurent.lecluse at unicaen.fr> + */ +class UpdateDdlCommand extends Command +{ + use BddAwareTrait; + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $bdd = $this->getBdd()->setLogger($io); + + $filters = [ + ]; + + $io->title('Génération de la DDL à partir de la base de données'); + + $ddl = $bdd->getDdl($filters); + + $ddl->saveToDir(); + + return Command::SUCCESS; + } +} \ No newline at end of file diff --git a/src/Command/UpdateDdlCommandFactory.php b/src/Command/UpdateDdlCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..c6402da14da5ad5f4613fd8fa64efb8d85e779b5 --- /dev/null +++ b/src/Command/UpdateDdlCommandFactory.php @@ -0,0 +1,32 @@ +<?php + +namespace Unicaen\BddAdmin\Command; + +use Psr\Container\ContainerInterface; +use Unicaen\BddAdmin\Bdd; + + +/** + * Description of UpdateDdlCommandFactory + * + * @author Laurent Lécluse <laurent.lecluse at unicaen.fr> + */ +class UpdateDdlCommandFactory +{ + + + /** + * @param ContainerInterface $container + * @param string $requestedName + * @param array|null $options + * + * @return UpdateBddCommand + */ + public function __invoke(ContainerInterface $container, $requestedName, $options = null): UpdateDdlCommand + { + $command = new UpdateDdlCommand; + $command->setBdd($container->get(Bdd::class)); + + return $command; + } +} \ No newline at end of file diff --git a/src/Ddl/Ddl.php b/src/Ddl/Ddl.php index c35c2b8b57f35fda565c9652d3510f09d1da3408..582da1e115fc0230fb679e086f66f9ddc14829f7 100644 --- a/src/Ddl/Ddl.php +++ b/src/Ddl/Ddl.php @@ -488,7 +488,13 @@ class Ddl implements Iterator, ArrayAccess if ($name != '.' && $name != '..') { $def = file_get_contents($dir . $type . '/' . $name); $name = substr($name, 0, -4); - $this->data[$type][$name] = ['name' => $name, 'definition' => $def]; + if (str_contains($name, '.')) { + [$schema, $vmname] = Util::explodedFullObjectName($name); + $this->data[$type][$name] = ['schema' => $schema, 'name' => $vmname, 'definition' => $def]; + }else{ + $this->data[$type][$name] = ['name' => $name, 'definition' => $def]; + } + } } } diff --git a/src/Driver/Postgresql/FunctionManager.php b/src/Driver/Postgresql/FunctionManager.php index 413b862b5f8ecbf6128f7d89111bda353df91533..06161dc4c82a17870d5b031ef4686664e3d411b7 100644 --- a/src/Driver/Postgresql/FunctionManager.php +++ b/src/Driver/Postgresql/FunctionManager.php @@ -106,6 +106,12 @@ class FunctionManager extends AbstractManager implements FunctionManagerInteface public function alter(array $old, array $new): void { + if (!isset($old['schema'])){ + $old['schema'] = 'public'; + } + if (!isset($new['schema'])){ + $new['schema'] = 'public'; + } if ($old != $new) { if ($this->sendEvent()->getReturn('no-exec')) return; diff --git a/src/Driver/Postgresql/MaterializedViewManager.php b/src/Driver/Postgresql/MaterializedViewManager.php index 3bbf40a3e29473d7bcb99607d5eee15c0f931053..cafef3bffc72bd7d9460538b496f0c124da29e51 100644 --- a/src/Driver/Postgresql/MaterializedViewManager.php +++ b/src/Driver/Postgresql/MaterializedViewManager.php @@ -78,7 +78,12 @@ class MaterializedViewManager extends AbstractManager implements MaterializedVie public function alter(array $old, array $new): void { if ($this->sendEvent()->getReturn('no-exec')) return; - + if (!isset($old['schema'])){ + $old['schema'] = 'public'; + } + if (!isset($new['schema'])){ + $new['schema'] = 'public'; + } if ($old != $new) { $this->drop($old); $this->create($new); diff --git a/src/Driver/Postgresql/ProcedureManager.php b/src/Driver/Postgresql/ProcedureManager.php index 3405c3368cefbde60db8d72e3a618b0b14fea8fd..e37d230e7efc2d932b17cd48c4576a91cf4fc2ca 100644 --- a/src/Driver/Postgresql/ProcedureManager.php +++ b/src/Driver/Postgresql/ProcedureManager.php @@ -79,6 +79,12 @@ class ProcedureManager extends AbstractManager implements ProcedureManagerIntefa public function alter(array $old, array $new): void { + if (!isset($old['schema'])){ + $old['schema'] = 'public'; + } + if (!isset($new['schema'])){ + $new['schema'] = 'public'; + } if ($old != $new) { if ($this->sendEvent()->getReturn('no-exec')) return; diff --git a/src/Driver/Postgresql/TableManager.php b/src/Driver/Postgresql/TableManager.php index 8b30de009bf0d5b349310a39e14827c54fac99c6..9208502a8a4d5b978783a440b9ddfa0e90c39903 100644 --- a/src/Driver/Postgresql/TableManager.php +++ b/src/Driver/Postgresql/TableManager.php @@ -80,8 +80,7 @@ class TableManager extends AbstractManager implements TableManagerInterface $q = "SELECT ns.nspname \"schema\", t.relname \"name\", - 'N' \"temporary\", - 'NO' logging, + t.relpersistence relpersistence, c.column_name cname, c.data_type \"type\", c.character_maximum_length length, @@ -113,8 +112,8 @@ class TableManager extends AbstractManager implements TableManagerInterface $data[$name] = [ 'schema' => $paq['schema'], 'name' => $paq['name'], - 'temporary' => $paq['temporary'] == 'YES', - 'logging' => $paq['logging'] == 'YES', + 'temporary' => $paq['relpersistence'] == 't', + 'logging' => $paq['relpersistence'] == 'p', 'commentaire' => $paq['commentaire'], 'sequence' => $paq['sequence'], 'columns' => [], @@ -578,9 +577,8 @@ class TableManager extends AbstractManager implements TableManagerInterface if ($this->sendEvent()->getReturn('no-exec')) return; if ($old['logging'] !== $new['logging']) { - throw new BddException("Il n'est pas possible actuellement de supprimer le logging d'une table ou de faire l'opération inverse"); - //$log = $new['logging'] ? 'LOGGING' : 'NOLOGGING'; - //$this->addQuery("ALTER TABLE $name $log", 'Modification du logging de la table ' . $name); + $sql = "ALTER TABLE $name SET ".($new['logging'] ? 'LOGGED' : 'UNLOGGED'); + $this->addQuery($sql, 'Modification du logging de la table ' . $name); } $newCols = array_diff(array_keys($new['columns']), array_keys($old['columns'])); diff --git a/src/Driver/Postgresql/TriggerManager.php b/src/Driver/Postgresql/TriggerManager.php index e42078311e4e9b85d7009258ac42b466197add1f..20a681c5c0dab4ac625b80ce132d7ca23be641d2 100644 --- a/src/Driver/Postgresql/TriggerManager.php +++ b/src/Driver/Postgresql/TriggerManager.php @@ -153,6 +153,12 @@ class TriggerManager extends AbstractManager implements TriggerManagerInterface public function alter(array $old, array $new): void { + if (!isset($old['schema'])){ + $old['schema'] = 'public'; + } + if (!isset($new['schema'])){ + $new['schema'] = 'public'; + } if ($old != $new) { if ($this->sendEvent()->getReturn('no-exec')) return; diff --git a/src/Driver/Postgresql/ViewManager.php b/src/Driver/Postgresql/ViewManager.php index a69159d3048a7ce27f8b55282f112543d71f3c0c..d8917d88e951fcf487b6e48a30339264b3d3c1a4 100644 --- a/src/Driver/Postgresql/ViewManager.php +++ b/src/Driver/Postgresql/ViewManager.php @@ -75,6 +75,12 @@ class ViewManager extends AbstractManager implements ViewManagerInterface public function alter(array $old, array $new): void { + if (!isset($old['schema'])){ + $old['schema'] = 'public'; + } + if (!isset($new['schema'])){ + $new['schema'] = 'public'; + } if ($old != $new) { if ($this->sendEvent()->getReturn('no-exec')) return; diff --git a/src/Logger/DefaultLogger.php b/src/Logger/DefaultLogger.php index cb82ff159b4665895cda3c4eab6bfdfb79c63fe3..4846a4917fd1fad32c667d896d56ea35d1c83244 100644 --- a/src/Logger/DefaultLogger.php +++ b/src/Logger/DefaultLogger.php @@ -54,7 +54,7 @@ class DefaultLogger implements LoggerInterface - public function error($e) + public function error(Throwable|string $e) { if ($e instanceof \Throwable) { $e = $e->getMessage(); diff --git a/src/Logger/LoggerAwareTrait.php b/src/Logger/LoggerAwareTrait.php index 6c335419a067ded783e84a44289a6737de2d013f..399a64ed6758ee734dcd637760ef53f5c842cf53 100644 --- a/src/Logger/LoggerAwareTrait.php +++ b/src/Logger/LoggerAwareTrait.php @@ -2,6 +2,8 @@ namespace Unicaen\BddAdmin\Logger; +use Symfony\Component\Console\Style\SymfonyStyle; + trait LoggerAwareTrait { @@ -24,8 +26,11 @@ trait LoggerAwareTrait * * @return self */ - public function setLogger(?LoggerInterface $logger): self + public function setLogger(null|LoggerInterface|SymfonyStyle $logger): self { + if ($logger instanceof SymfonyStyle){ + $logger = new SymfonyStyleLogger($logger); + } $this->logger = $logger; return $this; diff --git a/src/Logger/LoggerInterface.php b/src/Logger/LoggerInterface.php index 51b7b437333782e57a7998557c74d4466cfaeee4..0d0aa4bf545a7fa0cc6210b3cc84243bcb57f349 100644 --- a/src/Logger/LoggerInterface.php +++ b/src/Logger/LoggerInterface.php @@ -4,7 +4,7 @@ namespace Unicaen\BddAdmin\Logger; interface LoggerInterface { - public function error($e); + public function error(Throwable|string $e); diff --git a/src/Logger/SymfonyStyleLogger.php b/src/Logger/SymfonyStyleLogger.php new file mode 100644 index 0000000000000000000000000000000000000000..15307cf664ec0329b0f6f07a2c16674d0b87004e --- /dev/null +++ b/src/Logger/SymfonyStyleLogger.php @@ -0,0 +1,92 @@ +<?php + +namespace Unicaen\BddAdmin\Logger; + + +use Symfony\Component\Console\Style\SymfonyStyle; + +class SymfonyStyleLogger implements LoggerInterface +{ + protected ?string $lastMessage = null; + + protected bool $lastRewrite = false; + + protected SymfonyStyle $symfonyStyle; + + + + public function __construct(SymfonyStyle $symfonyStyle) + { + $this->symfonyStyle = $symfonyStyle; + } + + + + public function print($text, $color = null, $bgColor = null) + { + $this->symfonyStyle->write($text); + } + + + + public function println($text, $color = null, $bgColor = null) + { + $this->symfonyStyle->writeln($text); + } + + + + public function msg($message, bool $rewrite = false) + { + if ($rewrite) { + if ($this->lastMessage) { + $m = $message . str_pad('', strlen($this->lastMessage) - strlen($message) + 2) . "\r"; + } else { + $m = $message . "\r"; + } + $this->print($m); + } else { + $this->println($message); + } + $this->lastMessage = $message; + $this->lastRewrite = $rewrite; + } + + + + public function error(Throwable|string $e) + { + if ($e instanceof \Throwable) { + $e = $e->getMessage(); + } + if ($this->lastRewrite) $this->println(''); + $this->symfonyStyle->error($e); + } + + + + public function begin(string $title) + { + if ($this->lastMessage) { + $title .= str_pad('', strlen($this->lastMessage) - strlen($title) + 2); + } + $this->lastRewrite = false; + $this->lastMessage = null; + $this->symfonyStyle->title($title); + } + + + + public function end(?string $msg = null): void + { + if ($this->lastMessage && $this->lastRewrite) { + $msg .= str_pad('', strlen($this->lastMessage) - strlen($msg ?? '') + 2); + } + if (trim($msg)) { + $this->symfonyStyle->comment($msg); + } else { + $this->println(''); + } + } + +} \ No newline at end of file diff --git a/src/Migration/MigrationManager.php b/src/Migration/MigrationManager.php index 9edc1b05b743a2c004d7cb06a5fa308599cc22c1..f0b5569fe43417fcf8d5c90b129c690ce768a10e 100644 --- a/src/Migration/MigrationManager.php +++ b/src/Migration/MigrationManager.php @@ -11,7 +11,7 @@ class MigrationManager { use OptionsTrait; - const string OPTION_DIR = 'dir'; + const OPTION_DIR = 'dir'; protected Ddl $ref; @@ -174,7 +174,7 @@ class MigrationManager protected function getMigrationDir(): ?string { - $migrationDir = $this->getBdd()->getOption(Bdd::OPTION_MIGRATION_DIR); + $migrationDir = $this->getBdd()->getOption(self::OPTION_DIR); return $migrationDir; }