Introduction
On parle souvent des frameworks, APIs, librairies JavaScript, etc… mais très peu souvent de la partie “back” d’une application. Par là, j’entends serveur, base de données sur lesquels va tourner une application… Et quelquefois (souvent même je dirai), il faut mettre la main à la pâte pour pouvoir faire fonctionner tout ça car ce n’est pas forcément à l’ “administrateur système” ou à l’ “équipe production” de gérer entièrement cela et/ou auquel il faut déléguer cette partie très importante, sur laquelle repose et vit une application ! J’ajouterai même que c’est une étape incontournable lorsqu’on “touche” sérieusement à une application (que ça soit la création ou la maintenance (T.M.A.) de celle-ci).
Par contre, le choix du système d’exploitation et/ou du système de gestion de base de données (plus communément appelé S.G.B.D.) sur lesquels va s’appuyer le serveur et la B.D.D. est assez souvent imposé. Pourquoi ? Tout simplement car d’autres applications du parc de l’entreprise (petite ou grosse) pour laquelle on travaille utilisent déjà un S.G.B.D et un O.S particuliers…
Allez c’est parti !
Dans cette partie, je vais donc aborder le sujet de l’export/import d’une B.D.D., tâche qui peut être amenée à se répéter régulièrement lorsqu’on veut mettre à jour son environnement de recette et/ou de développement par rapport aux données de l’environnement de production.
Pour ma part, c’est un S.G.B.D. Oracle et un Windows Server sur lesquels je me suis appuyé ! Dans la suite de cet article, je vais donc vous montrer comment j’ai pu mettre en place un “script” complet et paramétrable (adaptable assez facilement).
Vous aurez donc certainement deviné que j’ai utilisé un langage de base : le batch (programmation MS-DOS (fichier d’extension .bat)) ! Certes il y a quelques notions de base à avoir mais l’appréhension et la compréhension de ce langage se font très rapidement et très facilement. De plus, il existe des tonnes et des tonnes de sites Web qui en parle…
J’en entends déjà certains dire “Ah mais on pourrait utiliser d’autres langages (Perl, Python, C (Why not !), etc…), des APIs, etc…” ! Oui je pourrai être d’accord avec eux mais ici le but était de réaliser un script fonctionnel rapidement et qui soit simple d’utilisation et d’adaptation (pas besoin d’installer tel ou tel trucs pour faire fonctionner le programme, juste régler et adapter quelques petits paramètres) ! En gros, ce script n’a pas de vocation internationale, juste réaliser ce qu’il faut et rien de plus !
Pour commencer, j’ai d’abord mis en place l’arborescence du projet :
Voici quelques explications sur les différents éléments que l’on voit :
- le dossier bin contient les différents scripts nécessaires à l’import, la suppression et l’export d’une B.D.D..
- le dossier config contient le script qui contient les paramètres que l’on peut modifier suivant la configuration que l’on veut mettre en place.
- le dossier dump va contenir le fichier d’export de la B.D.D.
- le fichier export_import.bat est le fichier principale qui permet de lancer le programme ! Il contient également quelques paramètres modifiables.
- le dossier logs va contenir tout l’historique de la dernière exécution du programme.
- le fichier README.txt se passe de commentaires…
- le dossier sql contient les différents scripts SQL (dont un généré et qui est paramétrable) qui vont permettre de supprimer les données de la B.D.D dans laquelle on va importer des données fraîchement acquises.
Décomposons ensuite petit à petit chaque contenu des dossiers et fichiers qui sont énumérés ci-dessus.
On va donc commencer par le dossier bin :
Comme dit précédemment et comme vous pouvez le voir, celui-ci contient 3 fichiers qui effectuent respectivement : la suppression des données dans la B.D.D destinataire, l’export des données de la B.D.D. source et l’import de ces dernières dans la B.D.D. destinataire ! Rien de plus compliqué à part qu’ils utilisent des commandes MS-DOS spécifiques à Oracle…
- DropOracleServerTo.bat
1 2 3 4 5 6 7 8 9 | SET DB_HOSTNAME=BDD2_HOST SET SERVICE_NAME=BDD2_SERVICE SET DBMS_SCHEMA=BDD2_SCHEMA SET DBMS_PASSWORD=BDD2_PWD SET DROP_FILE=%1 SET LOG_FILE=%2 sqlplus -L %DBMS_SCHEMA%/%DBMS_PASSWORD%@//%DB_HOSTNAME%/%SERVICE_NAME% @%DROP_FILE% >> %LOG_FILE% |
- ExportOracleServerFrom.bat
1 2 3 4 5 6 7 8 9 10 11 12 13 | SET NLS_LANG=American_america.utf8 SET ORACLE_USER_LOGIN=BDD1_SCHEMA SET ORACLE_USER_PASSWORD=BDD1_PWD SET ORACLE_EZCONNECT=//BDD1_HOST:BDD1_PORT/BDD1_SERVICE SET DUMP_FILE=%1 SET LOG_FILE=%2 exp userid=%ORACLE_USER_LOGIN%/%ORACLE_USER_PASSWORD%@%ORACLE_EZCONNECT% ^ file=%DUMP_FILE% ^ log=%LOG_FILE% ^ statistics=none |
- ImportOracleServerTo.bat
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | SET NLS_LANG=American_america.utf8 SET ORACLE_SRC_USER=BDD1_SCHEMA SET ORACLE_USER_LOGIN=BDD2_SCHEMA SET ORACLE_USER_PASSWORD=BDD2_PWD SET ORACLE_EZCONNECT=//BDD2_HOST:BDD2_PORT/BDD2_SERVICE SET DUMP_FILE=%1 SET LOG_FILE=%2 imp userid=%ORACLE_USER_LOGIN%/%ORACLE_USER_PASSWORD%@%ORACLE_EZCONNECT% ^ file=%DUMP_FILE% ^ FROMUSER=%ORACLE_SRC_USER% ^ TOUSER=%ORACLE_USER_LOGIN% ^ log=%LOG_FILE% ^ ignore=y |
Je ne vais pas m’attarder à expliquer ce que fait chaque script mais vous pouvez déjà avoir un aperçu de ce que ça donne dans mon cas d’utilisation (avec les bonnes valeurs qu’il faut bien sûr :-))! Et comme vous pouvez le voir également, le batch n’est pas un langage compliqué et ressemble sensiblement au bash !
Pour ce qui est du dossier config, il contient en tout et pour tout un fichier de configuration et de vérifications de paramètres :
- config.bat
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | SET ErrorLevel=0 REM <--------------------------- REM : LOG INFO SET FROM=Server From SET TO=Server To REM ---------------------------> REM <--------------------------- REM : DIRECTORIES SET BIN_DIRECTORY=%CURRENT_DIRECTORY%bin REM SET SQL_DIRECTORY=%CURRENT_DIRECTORY%\sql REM SET DUMP_DIRECTORY=%CURRENT_DIRECTORY%\dump REM SET LOGS_DIRECTORY=%CURRENT_DIRECTORY%\logs if not exist %BIN_DIRECTORY% ( echo %DATE% %TIME% [ERROR] : Le dossier %BIN_DIRECTORY% n'existe pas ! echo %DATE% %TIME% [ERROR] : Le dossier %BIN_DIRECTORY% n'existe pas ! >> %ERROR_FILE% SET ErrorLevel=9 goto :eof ) echo %BIN_DIRECTORY% REM echo %SQL_DIRECTORY% REM echo %DUMP_DIRECTORY% REM echo %LOGS_DIRECTORY% REM ---------------------------> REM <--------------------------- REM : BATCH SET EXPORT_FILE=%BIN_DIRECTORY%\ExportOracleServerTo.bat SET IMPORT_FILE=%BIN_DIRECTORY%\ImportOracleServerTo.bat SET DROP_FILE=%BIN_DIRECTORY%\DropOracleServerFrom.bat echo %EXPORT_FILE% echo %IMPORT_FILE% echo %DROP_FILE% REM ---------------------------> REM <--------------------------- REM : SQL SCRIPT set SELECT_DROP_FILE_SQL=%SQL_DIRECTORY%\SELECT_DROP.sql echo %SELECT_DROP_FILE_SQL% REM ---------------------------> REM <--------------------------- REM : DATE AND TIME if "%TIME:~0,1%" == " " ( SET HOUR=0%TIME:~1,1% ) else ( SET HOUR=%TIME:~0,2% ) SET CURRENT_DATE=%DATE:~7,2%-%DATE:~4,2%-%DATE:~10,4% SET BEGIN_DATE_AND_TIME=%CURRENT_DATE%_%HOUR%-%TIME:~3,2% echo %CURRENT_DATE% echo %BEGIN_DATE_AND_TIME% REM ---------------------------> REM <--------------------------- REM : LOGS FILES SET LOGS_FILE=%LOGS_DIRECTORY%\logs_%BEGIN_DATE_AND_TIME%.log SET EXPORT_LOG_FILE=%LOGS_DIRECTORY%\ExportOracle_%BEGIN_DATE_AND_TIME%.log SET IMPORT_LOG_FILE=%LOGS_DIRECTORY%\ImportOracle_%BEGIN_DATE_AND_TIME%.log SET DROP_LOG_FILE=%LOGS_DIRECTORY%\DropOracle_%BEGIN_DATE_AND_TIME%.log echo %LOGS_FILE% echo %EXPORT_LOG_FILE% echo %IMPORT_LOG_FILE% echo %DROP_LOG_FILE% REM ---------------------------> REM <--------------------------- REM : DUMP FILE SET DUMP_FILE=%DUMP_DIRECTORY%\dump_%BEGIN_DATE_AND_TIME%.dmp echo %DUMP_FILE% REM ---------------------------> |
Celui-ci a donc pour but de vérifier que les dossiers et fichiers nécessaires à la bonne exécution du programme sont présents. Il définit aussi quelques variables communes à d’autres scripts ! Et pour info, la commande “REM” permet de commenter une ligne (pas vraiment visible ici).
Je passe maintenant directement au fichier principal export_import.bat :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | @echo off REM PAUSE REM <--------------------------- REM : TO BE DEFINED FOR THE environment REM SET CURRENT_DIRECTORY="DISK:\PATH" SET CURRENT_DIRECTORY=%~dp0 SET ERROR_FILE=%CURRENT_DIRECTORY%ERROR.log SET CONFIG_DIRECTORY=%CURRENT_DIRECTORY%config if not exist %CONFIG_DIRECTORY% ( echo %DATE% %TIME% [ERROR] : Le dossier %CONFIG_DIRECTORY% n'existe pas ! echo %DATE% %TIME% [ERROR] : Le dossier %CONFIG_DIRECTORY% n'existe pas ! >> %ERROR_FILE% goto :eof ) SET CONFIG_FILE=%CONFIG_DIRECTORY%\config.bat echo %CURRENT_DIRECTORY% echo %CONFIG_DIRECTORY% echo %CONFIG_FILE% REM ---------------------------> REM <--------------------------- REM : logs, dump, sql DIRECTORIES SET LOGS_DIRECTORY=%CURRENT_DIRECTORY%logs SET DUMP_DIRECTORY=%CURRENT_DIRECTORY%dump SET SQL_DIRECTORY=%CURRENT_DIRECTORY%sql if not exist %SQL_DIRECTORY% ( echo %DATE% %TIME% [ERROR] : Le dossier %SQL_DIRECTORY% n'existe pas ! echo %DATE% %TIME% [ERROR] : Le dossier %SQL_DIRECTORY% n'existe pas ! >> %ERROR_FILE% goto :eof ) echo %LOGS_DIRECTORY% echo %DUMP_DIRECTORY% echo %SQL_DIRECTORY% REM ---------------------------> REM <--------------------------- REM : DROP.sql FILE SET DROP_FILE_SQL=%SQL_DIRECTORY%\DROP.sql echo %DROP_FILE_SQL% REM ---------------------------> REM <--------------------------- REM : echo %DATE% %TIME% : [INFO] Suppression du dossier de logs... rmdir /S /Q %LOGS_DIRECTORY% echo %DATE% %TIME% : [INFO] Creation du dossier de logs. mkdir %LOGS_DIRECTORY% echo %DATE% %TIME% : [INFO] Suppression du dossier de dump... rmdir /S /Q %DUMP_DIRECTORY% echo %DATE% %TIME% : [INFO] Creation du dossier de dump. mkdir %DUMP_DIRECTORY% echo %DATE% %TIME% : [INFO] Suppression du fichier SQL genere '%DROP_FILE_SQL%'... del %DROP_FILE_SQL% REM ---------------------------> echo %DATE% %TIME% : [INFO] Initialisation du script... call %CONFIG_FILE% if %ErrorLevel% == 9 ( echo %DATE% %TIME% [ERROR] : Erreur dans l'initialisation du script. echo %DATE% %TIME% [ERROR] : Erreur dans l'initialisation du script. >> %ERROR_FILE% goto :eof ) echo %CURRENT_DATE% %TIME% : [INFO] Initialisation du script OK. >> %LOGS_FILE% echo %CURRENT_DATE% %TIME% : [INFO] Suppression du dossier de logs OK. >> %LOGS_FILE% echo %CURRENT_DATE% %TIME% : [INFO] Creation du dossier de logs OK. >> %LOGS_FILE% echo %CURRENT_DATE% %TIME% : [INFO] Suppression du dossier de dump OK. >> %LOGS_FILE% echo %CURRENT_DATE% %TIME% : [INFO] Creation du dossier de dump OK. >> %LOGS_FILE% echo %CURRENT_DATE% %TIME% : [INFO] Suppression du fichier SQL genere %DROP_FILE_SQL% OK. >> %LOGS_FILE% REM PAUSE REM for the next... REM - 0 = Success REM - 1 = Not Supported REM - 2 = Access Denied REM - 3 = Dependent Services Running REM - 4 = Invalid Service Control REM - 5 = Service Cannot Accept Control REM - 6 = Service Not Active REM - 7 = Service Request Timeout REM - 8 = Unknown Failure REM - 9 = Path Not Found REM - 10 = Service Already Running REM - 11 = Service Database Locked REM - 12 = Service Dependency Deleted REM - 13 = Service Dependency Failure REM - 14 = Service Disabled REM - 15 = Service Logon Failure REM - 16 = Service Marked For Deletion REM - 17 = Service No Thread REM - 18 = Status Circular Dependency REM - 19 = Status Duplicate Name REM - 20 = Status Invalid Name REM - 21 = Status Invalid Parameter REM - 22 = Status Invalid Service Account REM - 23 = Status Service Exists REM - 24 = Service Already Paused echo %CURRENT_DATE% %TIME% : [INFO] Debut. echo %CURRENT_DATE% %TIME% : [INFO] Debut. >> %LOGS_FILE% echo %CURRENT_DATE% %TIME% : [INFO] Export de la BDD "%FROM%". echo %CURRENT_DATE% %TIME% : [INFO] Export de la BDD "%FROM%". >> %LOGS_FILE% REM 2 params : bat + log files call %EXPORT_FILE% %DUMP_FILE% %EXPORT_LOG_FILE% if NOT %ErrorLevel% == 0 ( REM 3 -> WARNING if NOT %ErrorLevel% == 3 ( echo %CURRENT_DATE% %TIME% : [ERROR] ErrorLevel : %ErrorLevel%. echo %CURRENT_DATE% %TIME% : [ERROR] ErrorLevel : %ErrorLevel%. >> %LOGS_FILE% echo %CURRENT_DATE% %TIME% : [ERROR] Export de la BDD "%FROM%" : KO. echo %CURRENT_DATE% %TIME% : [ERROR] Export de la BDD "%FROM%" : KO. >> %LOGS_FILE% goto :ERROR ) ) echo %CURRENT_DATE% %TIME% : [INFO] Export de la BDD "%FROM%" : OK. echo %CURRENT_DATE% %TIME% : [INFO] Export de la BDD "%FROM%" : OK. >> %LOGS_FILE% REM PAUSE echo %CURRENT_DATE% %TIME% : [INFO] Suppression de la BDD "%TO%". echo %CURRENT_DATE% %TIME% : [INFO] Suppression de la BDD "%TO%". >> %LOGS_FILE% REM 2 params : sql + log files call %DROP_FILE% %SELECT_DROP_FILE_SQL% %DROP_LOG_FILE% if NOT %ErrorLevel% == 0 ( REM 3 -> WARNING if NOT %ErrorLevel% == 3 ( echo %CURRENT_DATE% %TIME% : [ERROR] ErrorLevel : %ErrorLevel%. echo %CURRENT_DATE% %TIME% : [ERROR] ErrorLevel : %ErrorLevel%. >> %LOGS_FILE% echo %CURRENT_DATE% %TIME% : [ERROR] Suppression de la BDD "%TO%" : KO. echo %CURRENT_DATE% %TIME% : [ERROR] Suppression de la BDD "%TO%" : KO. >> %LOGS_FILE% goto :ERROR ) ) echo %CURRENT_DATE% %TIME% : [INFO] Suppression de la BDD "%TO%" : OK. echo %CURRENT_DATE% %TIME% : [INFO] Suppression de la BDD "%TO%" : OK. >> %LOGS_FILE% REM PAUSE echo %CURRENT_DATE% %TIME% : [INFO] Import de la BDD "%FROM%" vers "%TO%". echo %CURRENT_DATE% %TIME% : [INFO] Import de la BDD "%FROM%" vers "%TO%". >> %LOGS_FILE% REM 2 params : bat + log files call %IMPORT_FILE% %DUMP_FILE% %IMPORT_LOG_FILE% if NOT %ErrorLevel% == 0 ( REM 3 -> WARNING if NOT %ErrorLevel% == 3 ( echo %CURRENT_DATE% %TIME% : [ERROR] ErrorLevel : %ErrorLevel%. echo %CURRENT_DATE% %TIME% : [ERROR] ErrorLevel : %ErrorLevel%. >> %LOGS_FILE% echo %CURRENT_DATE% %TIME% : [ERROR] Import de la BDD "%FROM%" vers "%TO%" : KO. echo %CURRENT_DATE% %TIME% : [ERROR] Import de la BDD "%FROM%" vers "%TO%" : KO. >> %LOGS_FILE% goto :ERROR ) ) echo %CURRENT_DATE% %TIME% : [INFO] Import de la BDD "%FROM%" vers "%TO%" : OK. echo %CURRENT_DATE% %TIME% : [INFO] Import de la BDD "%FROM%" vers "%TO%" : OK. >> %LOGS_FILE% goto :END :ERROR echo %CURRENT_DATE% %TIME% : [ERROR] Le script ne s'est pas termine correctement... echo %CURRENT_DATE% %TIME% : [INFO] Le script ne s'est pas termine correctement... >> %LOGS_FILE% :END echo %CURRENT_DATE% %TIME% : [INFO] Fin. echo %CURRENT_DATE% %TIME% : [INFO] Fin. >> %LOGS_FILE% |
Ce fichier dirige toutes les actions que l’on veut effectuer : appel aux différents scripts de suppression, d’import et d’export, historisation de ce qui s’est passé, etc… Il gère également l’exécution multiple du programme (nettoyage des dossiers logs, dump et sql) et procède également à quelques vérifications, comme le fichier config.bat précédemment décrit.
Enfin, on va terminer par le dossier sql qui contient un fichier de base SELECT_DROP.sql et un fichier généré DROP.sql après l’exécution du script :
- SELECT_DROP.sql
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | SET echo OFF SET termout OFF SET feedback OFF SET heading OFF SET pages 0 SET LINES 0 SET pagesize 0 SET pause OFF SET colsep ';' -- TO BE DEFINED FOR THE environment (cf. line 34 too) spool '%DROP_FILE_SQL%' SELECT 'DROP TABLE ' || USER || '.' || TABLE_NAME || ' CASCADE CONSTRAINTS;' FROM USER_TABLES; SELECT 'DROP SEQUENCE ' || USER || '.' || SEQUENCE_NAME || ';' FROM USER_SEQUENCES; SELECT 'DROP PROCEDURE ' || USER || '.' || OBJECT_NAME || ';' FROM USER_PROCEDURES WHERE object_type ='PROCEDURE'; SELECT 'DROP VIEW ' || USER || '.' || VIEW_NAME || ' CASCADE CONSTRAINTS;' FROM user_views; spool OFF SET termout ON SET feedback ON SET heading ON SET pause ON @'%DROP_FILE_SQL%' exit |
Je l’ai déjà dit mais je le répète, certaines actions présentes dans le code ci-dessus sont spécifiques à Oracle. Pour ce qui est de la compréhension de ce script SQL, il permet de choisir les types que l’on veut supprimer (tables, séquences, procédures, vues, …) et d’extraire ainsi que de formater dans un autre fichier SQL (vous comprenez pourquoi il y a un fichier généré ;-)) le code permettant d’exécuter ladite suppression. Un exemple vaut mieux qu’un long discours :
1 2 3 4 5 6 7 8 9 10 11 12 13 | DROP TABLE nom_du_schema.nom_de_la_table CASCADE CONSTRAINTS; ... DROP PROCEDURE nom_du_schema.nom_de_la_procedure; ... DROP VIEW nom_du_schema.nom_de_la_vue CASCADE CONSTRAINTS; ... DROP SEQUENCE nom_du_schema.nom_de_la_sequence; |
Conclusion
Certes, la mise en place de ce programme prend un peu de temps du fait du langage et de ses quelques subtilités, mais au final vous êtes largement gagnant côté temps et performances ! En effet, au lieu de faire toutes les actions précédentes “à la main” :-(, c’est-à-dire :
- Exécuter un script qui va exporter les données de la B.D.D. source.
- Supprimer les données de la B.D.D. destinataire via un outil (DbVisualizer dans mon cas) ou via un script (comme décrit précédemment).
- Exécuter un dernier script qui va importer les nouvelles données dans la B.D.D destinataire.
Cela prend un certain temps quand même et demande votre présence pour faire les différentes actions…
De plus vous pouvez programmer ce script dans une tâche planifiée (ce que j’ai fait personnellement) pour qu’il s’exécute quand vous le voulez (tous les jours, une fois par semaine, juste du lundi au vendredi, une fois par mois, …).
Ce script est encore optimisable et pourrait même contenir quelques fonctionnalités en plus comme compter le nombre de lignes en plus (ou en moins) pour chaque table, etc…
Si vous avez des questions ou des remarques, n’hésitez pas !