Commit e8660176 authored by Bertrand Gauthier's avatar Bertrand Gauthier
Browse files

Eclatement de la commande generate-db-scripts en 4 commandes : vidage de...

Eclatement de la commande generate-db-scripts en 4 commandes : vidage de schéma, création de schéma avec/sans contraintes de ref, création des contraintes de ref, inserts de données.
parent 0c929677
Loading
Loading
Loading
Loading
+29 −5
Original line number Diff line number Diff line
@@ -31,13 +31,37 @@ class Module implements ConsoleUsageProviderInterface
    {
        return [
            // command
            'generate-db-scripts --src-schema=  --dst-schema= [--output-dir=] [--connection=]' =>
                "Générer les scripts SQL de vidage, création, peuplement d'un schéma destination à partir d'un schéma source.",
            'generate-script-for-schema-clearing --connection= [--output-dir=]' =>
                "Générer les scripts SQL de vidage d'un schéma.",
            // parameters
            ['--src-schema', "Requis. Nom du schema source, ex: 'SYGAL'."],
            ['--dst-schema', "Requis. Nom du schema destination, ex: 'SYGAL_TEST'."],
            ['--connection', "Requis. Nom de la connexion Doctrine concernée, ex: 'doctrine.connection.orm_default'."],
            ['--output-dir', "Facultatif. Chemin du répertoire où seront générés les scripts. Par défaut: '/tmp'."],

            // command
            'generate-script-for-schema-creation --src-connection= --dst-connection= [--ref-constraints-included=] [--output-dir=]' =>
                "Générer les scripts SQL de création d'un schéma destination à l'image d'un schéma source.",
            // parameters
            ['--src-connection', "Requis. Nom de la connexion Doctrine source, ex: 'doctrine.connection.orm_default'."],
            ['--dst-connection', "Requis. Nom de la connexion Doctrine destination, ex: 'doctrine.connection.orm_default'."],
            ['--ref-constraints-included', "Facultatif. Faut-il inclure la génération des contraintes de référence ? Valeur par défaut: 1."],
            ['--output-dir', "Facultatif. Chemin du répertoire où seront générés les scripts. Par défaut: '/tmp'."],

            // command
            'generate-script-for-ref-constraints-creation --src-connection= --dst-connection= [--output-dir=]' =>
                "Générer les scripts SQL de création des contraintes de référence d'un schéma source dans un schéma destination.",
            // parameters
            ['--src-connection', "Requis. Nom de la connexion Doctrine source, ex: 'doctrine.connection.orm_default'."],
            ['--dst-connection', "Requis. Nom de la connexion Doctrine destination, ex: 'doctrine.connection.orm_default'."],
            ['--output-dir', "Facultatif. Chemin du répertoire où seront générés les scripts. Par défaut: '/tmp'."],

            // command
            'generate-scripts-for-data-inserts --src-connection=  --dst-connection= --tables= [--output-dir=]' =>
                "Générer les scripts SQL d'insertion des données d'un schéma source dans un schéma destination.",
            // parameters
            ['--src-connection', "Requis. Nom de la connexion Doctrine source, ex: 'doctrine.connection.orm_default'."],
            ['--dst-connection', "Requis. Nom de la connexion Doctrine destination, ex: 'doctrine.connection.orm_default'."],
            ['--tables', "Requis. Noms des tables concernées séparés par une virgule, ex: 'ACTEUR,ATTESTATION,DIFFUSION'."],
            ['--output-dir', "Facultatif. Chemin du répertoire où seront générés les scripts. Par défaut: '/tmp'."],
            ['--connection', "Facultatif. Nom de la connexion Doctrine à utiliser. Par défaut: 'doctrine.connection.orm_default'."],
        ];
    }
}
+33 −3
Original line number Diff line number Diff line
@@ -44,13 +44,43 @@ return [
    'console' => [
        'router' => [
            'routes' => [
                'generate-db-scripts' => [
                'generateScriptForSchemaClearingConsole' => [
                    'type' => Simple::class,
                    'options' => [
                        'route'    => 'generate-db-scripts --src-schema=  --dst-schema= [--output-dir=] [--connection=]',
                        'route'    => 'generate-script-for-schema-clearing --connection= [--output-dir=]',
                        'defaults' => [
                            'controller' => IndexController::class,
                            'action'     => 'generateDbScriptsConsole',
                            'action'     => 'generateScriptForSchemaClearingConsole',
                        ],
                    ],
                ],
                'generateScriptForSchemaCreationConsole' => [
                    'type' => Simple::class,
                    'options' => [
                        'route'    => 'generate-script-for-schema-creation --src-connection= --dst-connection= [--ref-constraints-included=] [--output-dir=]',
                        'defaults' => [
                            'controller' => IndexController::class,
                            'action'     => 'generateScriptForSchemaCreationConsole',
                        ],
                    ],
                ],
                'generateScriptForRefConstraintsCreationConsole' => [
                    'type' => Simple::class,
                    'options' => [
                        'route'    => 'generate-script-for-ref-constraints-creation --src-connection= --dst-connection= [--output-dir=]',
                        'defaults' => [
                            'controller' => IndexController::class,
                            'action'     => 'generateScriptForRefConstraintsCreationConsole',
                        ],
                    ],
                ],
                'generateScriptsForDataInsertsConsole' => [
                    'type' => Simple::class,
                    'options' => [
                        'route'    => 'generate-scripts-for-data-inserts --src-connection= --dst-connection= --tables= [--output-dir=]',
                        'defaults' => [
                            'controller' => IndexController::class,
                            'action'     => 'generateScriptsForDataInsertsConsole',
                        ],
                    ],
                ],
+100 −97
Original line number Diff line number Diff line
@@ -3,8 +3,6 @@
namespace UnicaenOracle\Controller;

use Doctrine\DBAL\Connection;
use Doctrine\ORM\NoResultException;
use UnicaenApp\Exception\RuntimeException;
use UnicaenOracle\Service\Traits\DataServiceAwareTrait;
use UnicaenOracle\Service\Traits\SchemaServiceAwareTrait;
use Zend\Log\LoggerAwareTrait;
@@ -19,113 +17,87 @@ class IndexController extends AbstractActionController
    /**
     * Action en mode CLI.
     */
    public function generateDbScriptsConsoleAction()
    public function generateScriptForSchemaClearingConsoleAction()
    {
        $srcSchema = $this->params('src-schema'); // ex: 'SYGAL'
        $dstSchema = $this->params('dst-schema'); // ex: 'SYGAL_TEST'
        $destDir = $this->params('output-dir', '/tmp'); // ex: '/tmp'
        $connName = $this->params('connection', 'doctrine.connection.orm_default');

        /** @var Connection $srcSchemaConn */
        $srcSchemaConn = $this->getServiceLocator()->get($connName);
        /** @var Connection $connection */
        $connection = $this->getServiceLocator()->get($connName);
        $schemaName = $this->schemaService->extractSchemaNameFromConnection($connection);

        $this->log("=================================");
        $this->log("Database schema scripts generator");
        $this->log("=================================");
        $this->log("=====================================");
        $this->log(" Db schema clearing script generator");
        $this->log("=====================================");

        /**
         * Schema clearing.
         */
        $this->log("# Generating database clearing scripts...");
        $outputFilePath = $destDir . "/oracle-clear-schema-$dstSchema.sql";
        try {
            $this->schemaService->createSchemaClearingScriptFile($srcSchemaConn, $dstSchema, $outputFilePath);
        } catch (NoResultException $e) {
            throw new RuntimeException(
                "Impossible de générer le script de vidage du schéma '$dstSchema'. 
                Peut-être est-il déjà vide ou alors le schéma n'est pas accessible avec la connexion utilisée.");
        }
        $outputFilePath = $destDir . "/oracle-clear-schema-$schemaName.sql";
        $this->schemaService->createSchemaClearingScriptFile($connection, $outputFilePath);
        $this->log($outputFilePath);

        $this->log("Done.");

        exit(0);
    }

    /**
         * Schema creation.
     * Action en mode CLI.
     */
        $this->log("# Generating schema creation script...");
        $outputFilePath = $destDir . "/oracle-generate-schema-$dstSchema-from-$srcSchema.sql";
        $this->schemaService->createSchemaCreationScriptFile($srcSchemaConn, $srcSchema, $dstSchema, $outputFilePath);
    public function generateScriptForSchemaCreationConsoleAction()
    {
        $destDir = $this->params('output-dir', '/tmp'); // ex: '/tmp'
        $srcConnName = $this->params('src-connection', 'doctrine.connection.orm_default');
        $dstConnName = $this->params('dst-connection', 'doctrine.connection.orm_default');
        $refConstraintsIncluded = (bool) $this->params('ref-constraints-included', 1);

        /** @var Connection $srcConn */
        /** @var Connection $dstConn */
        $srcConn = $this->getServiceLocator()->get($srcConnName);
        $dstConn = $this->getServiceLocator()->get($dstConnName);

        $srcSchemaName = $this->schemaService->extractSchemaNameFromConnection($srcConn);
        $dstSchemaName = $this->schemaService->extractSchemaNameFromConnection($dstConn);

        $this->log("=====================================");
        $this->log(" Db schema creation script generator");
        $this->log("=====================================");

        $this->log("# Generating schema creation script " . ($refConstraintsIncluded ? 'with' : 'without') . ' ref constraints...');
        $outputFilePath = $destDir . "/oracle-generate-schema-$dstSchemaName-from-$srcSchemaName.sql";
        $this->schemaService->createSchemaCreationScriptFile($srcConn, $dstConn, $refConstraintsIncluded, $outputFilePath);
        $this->log($outputFilePath);

        $this->log("Done.");

        exit(0);
    }

    /**
         * Data inserts.
     * Action en mode CLI.
     */
    public function generateScriptsForDataInsertsConsoleAction()
    {
        $destDir = $this->params('output-dir', '/tmp'); // ex: '/tmp'
        $tableNames = $this->params('tables');
        $srcConnName = $this->params('src-connection', 'doctrine.connection.orm_default');
        $dstConnName = $this->params('dst-connection', 'doctrine.connection.orm_default');

        /** @var Connection $srcConn */
        /** @var Connection $dstConn */
        $srcConn = $this->getServiceLocator()->get($srcConnName);
        $dstConn = $this->getServiceLocator()->get($dstConnName);

        $srcSchemaName = $this->schemaService->extractSchemaNameFromConnection($srcConn);
        $dstSchemaName = $this->schemaService->extractSchemaNameFromConnection($dstConn);

        $this->log("===================================");
        $this->log(" Db data inserts scripts generator");
        $this->log("===================================");

        $this->log("# Generating data inserts scripts...");
        $tableNames = [
            'ACTEUR',
            'ATTESTATION',
            'CATEGORIE_PRIVILEGE',
            'DIFFUSION',
            'DOCTORANT',
            'DOCTORANT_COMPL',
            'DOMAINE_SCIENTIFIQUE',
            'ECOLE_DOCT',
            'ETABLISSEMENT',
            'ETABLISSEMENT_RATTACH',
            'FAQ',
            'FICHIER',
            'FINANCEMENT',
            'IMPORT_NOTIF',
            'IMPORT_OBS_NOTIF',
            'IMPORT_OBS_RESULT_NOTIF',
            'IMPORT_OBSERV',
            'IMPORT_OBSERV_RESULT',
            'INDICATEUR',
            'INDIVIDU',
            'INDIVIDU_ROLE',
            'MAIL_CONFIRMATION',
            'METADONNEE_THESE',
            'NATURE_FICHIER',
            'NOTIF',
            'NOTIF_RESULT',
            'ORIGINE_FINANCEMENT',
            'PRIVILEGE',
            'RDV_BU',
            'ROLE',
            'ROLE_MODELE',
            'ROLE_PRIVILEGE',
            'ROLE_PRIVILEGE_MODELE',
            'SOURCE',
            'STRUCTURE',
            'STRUCTURE_SUBSTIT',
            'SYNC_LOG',
            'SYNCHRO_LOG',
            'THESE',
            'TITRE_ACCES',
            'TMP_ACTEUR',
            'TMP_DOCTORANT',
            'TMP_ECOLE_DOCT',
            'TMP_ETABLISSEMENT',
            'TMP_FINANCEMENT',
            'TMP_INDIVIDU',
            'TMP_ORIGINE_FINANCEMENT',
            'TMP_ROLE',
            'TMP_STRUCTURE',
            'TMP_THESE',
            'TMP_TITRE_ACCES',
            'TMP_UNITE_RECH',
            'TMP_VARIABLE',
            'TYPE_STRUCTURE',
            'TYPE_VALIDATION',
            'UNITE_DOMAINE_LINKER',
            'UNITE_RECH',
            'UTILISATEUR',
            'VALIDATION',
            'VALIDITE_FICHIER',
            'VARIABLE',
            'VERSION_FICHIER',
            'WF_ETAPE',
        ];
        $outputFilePathTemplate = $destDir . "/oracle-data-insert-from-$srcSchema.%s-into-$dstSchema.sql";
        $outputFilePaths = $this->dataService->createDataInsertsScriptFile($srcSchemaConn, $dstSchema, $tableNames, $outputFilePathTemplate);
        $tableNames = array_filter(array_map('trim', explode(',', $tableNames)));
        $outputFilePathTemplate = $destDir . "/oracle-data-insert-from-$srcSchemaName.%s-into-$dstSchemaName.sql";
        $outputFilePaths = $this->dataService->createDataInsertsScriptFile($srcConn, $dstSchemaName, $tableNames, $outputFilePathTemplate);
        foreach ($outputFilePaths as $outputFilePath) {
            $this->log($outputFilePath);
        }
@@ -135,6 +107,37 @@ class IndexController extends AbstractActionController
        exit(0);
    }

    /**
     * Action en mode CLI.
     */
    public function generateScriptForRefConstraintsCreationConsoleAction()
    {
        $destDir = $this->params('output-dir', '/tmp'); // ex: '/tmp'
        $srcConnName = $this->params('src-connection', 'doctrine.connection.orm_default');
        $dstConnName = $this->params('dst-connection', 'doctrine.connection.orm_default');

        /** @var Connection $srcConn */
        /** @var Connection $dstConn */
        $srcConn = $this->getServiceLocator()->get($srcConnName);
        $dstConn = $this->getServiceLocator()->get($dstConnName);

        $srcSchemaName = $this->schemaService->extractSchemaNameFromConnection($srcConn);
        $dstSchemaName = $this->schemaService->extractSchemaNameFromConnection($dstConn);

        $this->log("==============================================");
        $this->log(" Db ref constraints creation script generator");
        $this->log("==============================================");

        $this->log("# Generating ref constraints creation script...");
        $outputFilePath = $destDir . "/oracle-generate-ref-constraints-$dstSchemaName-from-$srcSchemaName.sql";
        $this->schemaService->createRefConstraintsCreationScriptFile($srcConn, $dstConn, $outputFilePath);
        $this->log($outputFilePath);

        $this->log("Done.");

        exit(0);
    }

    /**
     * @param string $message
     */
+114 −32
Original line number Diff line number Diff line
@@ -4,7 +4,6 @@ namespace UnicaenOracle\Service;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Doctrine\ORM\NoResultException;
use UnicaenApp\Exception\RuntimeException;

/**
@@ -15,62 +14,131 @@ use UnicaenApp\Exception\RuntimeException;
class SchemaService
{
    /**
     * @param Connection $srcSchemaConn
     * @param string     $schemaName
     * @param Connection $conn
     * @return string
     */
    public function extractSchemaNameFromConnection(Connection $conn)
    {
        $connParams = $conn->getParams();
        $candidateKeys = ['CURRENT_SCHEMA', 'user'];

        foreach ($candidateKeys as $candidateKey) {
            if (isset($connParams[$candidateKey])) {
                return strtoupper($connParams[$candidateKey]);
            }
        }

        throw new RuntimeException(
            "L'une au moins des clés suivantes doit exister dans les paramètres de la connexion: " .
            implode(', ', $candidateKeys)
        );
    }

    /**
     * @param Connection $connection
     * @param string     $outputFilePath
     * @throws NoResultException
     */
    public function createSchemaClearingScriptFile(Connection $srcSchemaConn, $schemaName, $outputFilePath)
    public function createSchemaClearingScriptFile(Connection $connection, $outputFilePath)
    {
        try {
            $stmt = $srcSchemaConn->executeQuery($this->generateSQLForSchemaClearing($schemaName));
            $stmt = $connection->executeQuery($this->generateSQLForSchemaClearing($connection));
            $stmt->execute();
        } catch (DBALException $e) {
            throw new RuntimeException("Erreur!", null, $e);
        }
        $result = $stmt->fetchAll(\PDO::FETCH_COLUMN);

        // NB: si le résultat est vide, c'est que le schéma est lui-même vide,
        // ou alors  c'est un problème de droit du USER Oracle utilisé pour exécuter les commandes.
        if (count($result) === 0) {
            $result[] = "-- Noop! Schema is empty?";
        }

        $sql = implode('', $result);
        file_put_contents($outputFilePath, $sql);
    }

    /**
     * @param Connection $srcConn
     * @param Connection $dstConn
     * @param bool       $refConstraintsIncluded
     * @param string     $outputFilePath
     */
    public function createSchemaCreationScriptFile(Connection $srcConn, Connection $dstConn, $refConstraintsIncluded, $outputFilePath)
    {
        $srcSchemaName = $this->extractSchemaNameFromConnection($srcConn);
        $dstSchemaName = $this->extractSchemaNameFromConnection($dstConn);

        // config DBMS Metadata
        try {
            $stmt = $srcConn->executeQuery($this->generateSQLForDBMSMetadataPackageConfiguration());
            $stmt->execute();
        } catch (DBALException $e) {
            throw new RuntimeException("Erreur!", null, $e);
        }

        // génération DDL
        try {
            $stmt = $srcConn->prepare($this->generateSQLForSchemaCreation($dstConn, $refConstraintsIncluded));
            $stmt->execute();
        } catch (DBALException $e) {
            throw new RuntimeException("Erreur!", null, $e);
        }
        $result = $stmt->fetchAll(\PDO::FETCH_COLUMN);
        // NB: si le résultat est vide, c'est sans doute un problème de droit du USER Oracle utilisé pour exécuter les commandes.
        if (count($result) === 0) {
            throw new NoResultException();
            $result[] = "-- Noop!";
        }

        $sql = implode('', $result);
        $sql = str_replace("\"$srcSchemaName\"", "\"$dstSchemaName\"", $sql);
        file_put_contents($outputFilePath, $sql);
    }

    /**
     * @param Connection $srcSchemaConn
     * @param string     $srcSchemaName
     * @param string     $dstSchemaName
     * @param Connection $srcConn
     * @param Connection $dstConn
     * @param string     $outputFilePath
     */
    public function createSchemaCreationScriptFile(Connection $srcSchemaConn, $srcSchemaName, $dstSchemaName, $outputFilePath)
    public function createRefConstraintsCreationScriptFile(Connection $srcConn, Connection $dstConn, $outputFilePath)
    {
        $srcSchemaName = $this->extractSchemaNameFromConnection($srcConn);
        $dstSchemaName = $this->extractSchemaNameFromConnection($dstConn);

        // config DBMS Metadata
        try {
            $stmt = $srcSchemaConn->executeQuery($this->generateSQLForDBMSMetadataPackageConfiguration());
            $stmt = $srcConn->executeQuery($this->generateSQLForDBMSMetadataPackageConfiguration());
            $stmt->execute();
        } catch (DBALException $e) {
            throw new RuntimeException("Erreur!", null, $e);
        }
        // generate DDL

        // génération des contraintes de référence
        try {
            $stmt = $srcSchemaConn->prepare($this->generateSQLForSchemaCreation($srcSchemaName));
            $stmt = $srcConn->prepare($this->generateSQLForRefConstraintsCreation($dstConn));
            $stmt->execute();
        } catch (DBALException $e) {
            throw new RuntimeException("Erreur!", null, $e);
        }
        $result = $stmt->fetchAll(\PDO::FETCH_COLUMN);
        // NB: si le résultat est vide, c'est sans doute un problème de droit du USER Oracle utilisé pour exécuter les commandes
        // NB: si le résultat est vide, c'est sans doute un problème de droit du USER Oracle utilisé pour exécuter les commandes.
        if (count($result) === 0) {
            $result[] = "-- Noop!";
        }

        $sql = implode('', $result);
        $sql = str_replace("\"$srcSchemaName\"", "\"$dstSchemaName\"", $sql);
        file_put_contents($outputFilePath, $sql);
    }

    /**
     * @param string $schemaName
     * @param Connection $connection
     * @return string
     */
    function generateSQLForSchemaClearing($schemaName)
    function generateSQLForSchemaClearing(Connection $connection)
    {
        $schemaName = $this->extractSchemaNameFromConnection($connection);

        return <<<EOS
--
-- triggers
@@ -209,22 +277,25 @@ begin
  DBMS_METADATA.set_transform_param(DBMS_METADATA.SESSION_TRANSFORM, 'CONSTRAINTS_AS_ALTER', false); --
  DBMS_METADATA.set_transform_param(DBMS_METADATA.SESSION_TRANSFORM, 'CONSTRAINTS',          true);  -- including non-referential table constraints in the CREATE TABLE statement ?
  DBMS_METADATA.set_transform_param(DBMS_METADATA.SESSION_TRANSFORM, 'REF_CONSTRAINTS',      false); -- including referential constraints (foreign keys) in the CREATE TABLE statement ?
  DBMS_METADATA.set_transform_param(DBMS_METADATA.session_transform, 'FORCE',                true);  -- using the FORCE keyword in the CREATE VIEW statement ?
  DBMS_METADATA.set_transform_param(DBMS_METADATA.session_transform, 'TABLESPACE',           false); -- including tablespace clauses in the DDL ?
  DBMS_METADATA.set_transform_param(DBMS_METADATA.session_transform, 'SEGMENT_ATTRIBUTES',   false); -- including segment attributes clauses (physical attributes, storage attributes, tablespace, logging) in the DDL ?
  DBMS_METADATA.set_transform_param(DBMS_METADATA.session_transform, 'STORAGE',              false); -- including storage clauses in the DDL ?
  DBMS_METADATA.set_transform_param(DBMS_METADATA.SESSION_TRANSFORM, 'FORCE',                true);  -- using the FORCE keyword in the CREATE VIEW statement ?
  DBMS_METADATA.set_transform_param(DBMS_METADATA.SESSION_TRANSFORM, 'TABLESPACE',           false); -- including tablespace clauses in the DDL ?
  DBMS_METADATA.set_transform_param(DBMS_METADATA.SESSION_TRANSFORM, 'SEGMENT_ATTRIBUTES',   false); -- including segment attributes clauses (physical attributes, storage attributes, tablespace, logging) in the DDL ?
  DBMS_METADATA.set_transform_param(DBMS_METADATA.SESSION_TRANSFORM, 'STORAGE',              false); -- including storage clauses in the DDL ?
end;

EOS;
    }

    /**
     * @param string $schemaName
     * @param Connection $connection
     * @param bool       $refConstraintsIncluded
     * @return string
     */
    function generateSQLForSchemaCreation($schemaName)
    function generateSQLForSchemaCreation(Connection $connection, $refConstraintsIncluded = true)
    {
        return <<<EOS
        $schemaName = $this->extractSchemaNameFromConnection($connection);

        $sql = <<<EOS
--
-- sequences
--
@@ -347,20 +418,32 @@ SELECT DBMS_METADATA.get_ddl ('MATERIALIZED_VIEW', mview_name, owner) as sql
FROM   all_mviews
WHERE  owner      = '$schemaName'

EOS;

        if ($refConstraintsIncluded) {
            $refConstraintsSql = $this->generateSQLForRefConstraintsCreation($connection);
            $sql .= <<<EOS
UNION ALL


--
-- data inserts
--
$refConstraintsSql
EOS;
        }

--
-- [...]
--
        return $sql;
    }

    /**
     * @param Connection $connection
     * @return string
     */
    function generateSQLForRefConstraintsCreation(Connection $connection)
    {
        $schemaName = $this->extractSchemaNameFromConnection($connection);

        return <<<EOS
--
-- constraints
-- reference constraints
--

--select DBMS_METADATA.GET_DDL('CONSTRAINT', constraint_name, owner) as sql
@@ -373,7 +456,6 @@ from all_constraints
where owner = '$schemaName'
      and constraint_type = 'R'


EOS;

    }