diff --git a/VERSIONS_HOTLINE/141993 b/VERSIONS_HOTLINE/141993
new file mode 100644
index 0000000000000000000000000000000000000000..8d6a55d088543d0b13fcc4c70b9cdfb061a22406
--- /dev/null
+++ b/VERSIONS_HOTLINE/141993
@@ -0,0 +1 @@
+ - ticket #141993 : Maintenance : amélioration des outils de maintenance de Bokeh.
\ No newline at end of file
diff --git a/library/Class/Migration/UpdateConfig.php b/library/Class/Migration/UpdateConfig.php
new file mode 100644
index 0000000000000000000000000000000000000000..56c0cc088a43913f0eea2bfa8cbec444aeb0ee37
--- /dev/null
+++ b/library/Class/Migration/UpdateConfig.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class Class_Migration_UpdateConfig {
+
+  use
+    Trait_StaticFileSystem,
+    Trait_EchoError;
+
+
+  public function run($database_name) {
+    $this->echoError("===== Début de l'étape de mise à jour des fichiers de configuration =====\n");
+    $this
+      ->_setInBokehConfig('sgbd.config.dbname', $database_name)
+      ->_setInCosmoConfig('integration_base', $database_name);
+
+    $this->echoError("====== Fin de l'étape de mise à jour des fichiers de configuration =====\n\n");
+    return $this;
+  }
+
+
+  protected function _setInBokehConfig($key, $value) {
+    return $this->_writeNewDataInFile(ROOT_PATH . 'config.ini',
+                                      $key,
+                                      $value,
+                                      "/$key = .*/",
+                                      "$key = $value");
+  }
+
+  protected function _setInCosmoConfig($key, $value) {
+    return $this->_writeNewDataInFile(ROOT_PATH . 'cosmogramme/config.php',
+                                      $key,
+                                      $value,
+                                      "/$key=.*/",
+                                      "$key=$value");
+  }
+
+
+  protected function _writeNewDataInFile($file_path, $key, $value, $preg_search, $preg_replace) {
+    $this->echoError(sprintf("Mise à jour de %s dans le fichier de configartion %s\n",
+                             $key,
+                             $file_path));
+
+    $content = $this->getFileSystem()->file_get_contents($file_path);
+
+    if (false === $content) {
+      $this->echoError(sprintf("Mise à jour de %s dans le fichier de configartion %s impossible.\n"
+                               ."Le fichier n'existe pas\n",
+                               $key,
+                               $file_path));
+      return $this;
+    }
+
+    $new_content = preg_replace($preg_search, $preg_replace, $content);
+    $fh = $this->getFileSystem()->fopen($file_path, 'w');
+    $this->getFileSystem()->fwrite($fh, $new_content);
+
+    $this->echoError("[OK]\n");
+
+    return $this;
+  }
+}
diff --git a/library/Class/Migration/UpdateDatabaseAfterSelectDb.php b/library/Class/Migration/UpdateDatabaseAfterSelectDb.php
new file mode 100644
index 0000000000000000000000000000000000000000..bd9a8d71a14d107e42ebc2ff7a7fccdaa734a98d
--- /dev/null
+++ b/library/Class/Migration/UpdateDatabaseAfterSelectDb.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class Class_Migration_UpdateDatabaseAfterSelectDb {
+
+  use Trait_EchoError;
+
+
+  public function run() {
+    //reload config files
+    Bokeh_Engine::getInstance()->loadConfig();
+
+    $this->echoError(sprintf("===== Début de l'étape de mise à jour des variables dans la base de donnée : %s =====\n",
+                             array_at('dbname', Zend_Db_Table::getDefaultAdapter()->getConfig())));
+
+    $this->echoError("Mise à jour des variables de Cosmogramme.\n");
+
+    Class_CosmoVar::setValueOf('cache_path', './cosmogramme/fichiers/cache/');
+    Class_CosmoVar::setValueOf('log_path', './cosmogramme/fichiers/log/');
+    Class_CosmoVar::setValueOf('ftp_path', './cosmogramme/fichiers/transferts/');
+    Class_CosmoVar::setValueOf('integration_path', './cosmogramme/fichiers/integration/');
+
+    Class_CosmoVar::setValueOf('admin_login', 'admin');
+    Class_CosmoVar::setValueOf('admin_pwd', 'admin');
+
+    $this->echoError("[OK]\n");
+
+    $this->echoError("Mise à jour des variables d'administration.\n");
+
+    Class_AdminVar::set('ENABLE_COLLABORATIVE_BROWSING', '0');
+    Class_AdminVar::set('STATUS_REPORT_PUSH_URL', 'no');
+    Class_AdminVar::set('FORCE_HTTPS', '0');
+
+    $this->echoError("[OK]\n");
+
+    try {
+      $this->echoError("Mise à jour du mysql.proc.\n");
+      Zend_Db_Table::getDefaultAdapter()
+        ->query("update mysql.proc set definer='root@localhost'");
+    } catch (Exception $e) {
+      $this->echoError("Échec de la mise à jour du mysql.proc.\n"
+                       . $e->getMessage()
+                       . "/n");
+    }
+
+    $this->echoError("[OK]\n");
+
+    try {
+      $this->echoError("Suppression du trigger datemaj_notices_update;\n");
+      Zend_Db_Table::getDefaultAdapter()
+        ->query("drop trigger datemaj_notices_update;");
+    } catch (Exception $e) {
+      $this->echoError($e->getMessage() . "\n");
+    }
+
+    $this->echoError("[OK]\n");
+
+    $this->echoError("====== Fin de l'étape de mise à jour des variables =====\n\n");
+
+    return $this;
+  }
+}
diff --git a/library/startup.php b/library/startup.php
index 7a935d05a098340ad35bed0bbb963c68d393d1f8..28638cf42215f913d9968f315be19da0c69a3d34 100644
--- a/library/startup.php
+++ b/library/startup.php
@@ -30,18 +30,30 @@ function defineConstant($name, $value) {
 }
 
 
+
+
 class Bokeh_Engine {
 
+
   protected static
     $_zend_session_namespace,
-    $_php_command;
+    $_php_command,
+    $_instance;
 
 
   protected
+    $_warm_up_cache = false,
     $_config,
     $_front_controller;
 
 
+  public static function getInstance() {
+    if ( static::$_instance)
+      return static::$_instance;
+    return static::$_instance = new self();
+  }
+
+
   public function powerOn() {
     return
       $this
@@ -65,6 +77,9 @@ class Bokeh_Engine {
 
 
   function warmUp() {
+    if ( $this->_warm_up_cache )
+      return $this;
+
     require_once('Class/Url.php');
 
     defineConstant('BASE_URL',  Class_Url::baseUrl());
@@ -84,8 +99,13 @@ class Bokeh_Engine {
 
     Class_AdminVar::findAll();
 
-    return $this->setupLanguage()
-                ->setupStormObservers();
+    $this
+      ->setupLanguage()
+      ->setupStormObservers();
+
+    $this->_warm_up_cache = true;
+
+    return $this;
   }
 
 
@@ -152,7 +172,7 @@ class Bokeh_Engine {
     defineConstant('IMAGE_MAGICK_PATH', 'convert');
     defineConstant('THUMBNAIL_FIT_WIDTH_HEIGHT', '500');
 
-    defineConstant('PATCH_PATH', ROOT_PATH . '/cosmogramme/sql/patch/');
+    defineConstant('PATCH_PATH', ROOT_PATH . 'cosmogramme/sql/patch/');
     defineConstant('GIT_REALTIME',false);
     return $this;
   }
@@ -487,6 +507,7 @@ class Bokeh_Engine {
 
 
 
+
 class Bokeh_PhpCommand {
   public function header($text) {
     header($text);
@@ -505,6 +526,7 @@ class Bokeh_PhpCommand {
 
 
 
+
 class Bokeh_Session {
   public function get($session_id) {
     return new Zend_Session_Namespace($session_id);
@@ -512,8 +534,10 @@ class Bokeh_Session {
 }
 
 
+
+
 function setupOpac() {
-  return (new Bokeh_Engine())
+  return Bokeh_Engine::getInstance()
     ->powerOn()
     ->getFrontController();
 }
diff --git a/scripts/emacs/phafi-mode.el b/scripts/emacs/phafi-mode.el
index 7f396bb3d74631934a20cd77e93f638f486813aa..836dc4a73874b1209740446d4ee3fb24c08775b3 100644
--- a/scripts/emacs/phafi-mode.el
+++ b/scripts/emacs/phafi-mode.el
@@ -667,32 +667,12 @@
 	(client (car (popup-menu* (mysql-query "show databases;"  mysql-connection-info))))
 	(rootdir (concat (file-name-directory (symbol-file 'phafi-mode)) "../../"))
 	)
-    (phafi-replace-in-file "sgbd.config.dbname.*"
-			   (concat "sgbd.config.dbname = " client)
-			   (concat rootdir "config.ini"))
-    (phafi-replace-in-file "integration_base.*"
-			   (concat "integration_base=" client)
-			   (concat rootdir "cosmogramme/config.php"))
-    (mysql-query 
-     (concat 
-      "connect " client ";"
-      "update variables set valeur='./cosmogramme/fichiers/cache/' where clef='cache_path';"  
-      "update variables set valeur='./cosmogramme/fichiers/log/' where clef='log_path';"
-      "update variables set valeur='./cosmogramme/fichiers/transferts/' where clef='ftp_path';"
-      "update variables set valeur='./cosmogramme/fichiers/integration/' where clef='integration_path';"
-      "update variables set valeur='admin' where clef='admin_login';"  
-      "update variables set valeur='admin' where clef='admin_pwd';"
-      "update mysql.proc set definer='root@localhost';"
-      "replace into bib_admin_var (clef, valeur) values('ENABLE_COLLABORATIVE_BROWSING', '0');"
-      "replace into bib_admin_var (clef, valeur) values('STATUS_REPORT_PUSH_URL', 'no');"
-      "replace into bib_admin_var (clef, valeur) values('FORCE_HTTPS', '0');"
-      "drop trigger datemaj_notices_update;")
-     mysql-connection-info)
-
 
+    
     (async-shell-command 
      (concat "cd " (phafi-root-dir) ";"
-	     "php ./scripts/upgrade_db.php"))
+	     "php ./scripts/select_db.php "
+	     client))
     )
   )
 
diff --git a/scripts/select_db.php b/scripts/select_db.php
new file mode 100644
index 0000000000000000000000000000000000000000..c576cfc081a2754066742d31a7e484b3dd467446
--- /dev/null
+++ b/scripts/select_db.php
@@ -0,0 +1,14 @@
+<?php
+if ( ! isset($argv[1]))
+  return;
+
+$database_name = $argv[1];
+
+require('includes.php');
+
+Bokeh_Engine::getInstance()->warmUp();
+
+(new Class_Migration_UpdateConfig)->run($database_name);
+(new Class_Migration_UpdateDatabaseAfterSelectDb)->run();
+
+include('./scripts/upgrade_db.php');
diff --git a/scripts/upgrade_db.php b/scripts/upgrade_db.php
index 32a18a17706227c58ef5c877a5686ab484de8477..e6822cde512c7cebe199735f81d04dc0f68e3b17 100644
--- a/scripts/upgrade_db.php
+++ b/scripts/upgrade_db.php
@@ -2,7 +2,8 @@
 require('includes.php');
 
 define("BASE_URL", "/");
-(new Bokeh_Engine())
+
+Bokeh_Engine::getInstance()
   ->warmUp()
   ->setupControllerActionHelper()
   ->setupFrontController();
diff --git a/tests/library/Class/Migration/141993_config.ini b/tests/library/Class/Migration/141993_config.ini
new file mode 100644
index 0000000000000000000000000000000000000000..9fafc28d86c90a963fb74fa058ed1cf74ebecb8f
--- /dev/null
+++ b/tests/library/Class/Migration/141993_config.ini
@@ -0,0 +1,28 @@
+[global]
+timeZone = Europe/Paris
+locale = fr_FR.UTF-8
+
+; SMTP
+; mail.transport.smtp.host =
+
+; Used by global http client
+proxy.host = 
+proxy.port = 
+proxy.user = 
+proxy.pass =
+
+; SGBD
+sgbd.adapter = mysqli
+sgbd.config.host = bokeh-mysql-test
+sgbd.config.port = 3306
+sgbd.config.username = test
+sgbd.config.password = test
+sgbd.config.dbname = bokeh_test
+
+; Advanced
+amber.deploy = 0
+experimental_dev = true
+
+[local : global]				
+
+[production : global]
diff --git a/tests/library/Class/Migration/141993_cosmogramme_config.php b/tests/library/Class/Migration/141993_cosmogramme_config.php
new file mode 100644
index 0000000000000000000000000000000000000000..96ca387b3b70a752ae90db724524dbbae09f2fbf
--- /dev/null
+++ b/tests/library/Class/Migration/141993_cosmogramme_config.php
@@ -0,0 +1,16 @@
+<?php
+print("<center>");
+print("<br><br><br><br><br>");
+print('<div style="font-family:Verdana;font-size:20pt;color:red;font-weight:bold;">Accès interdit</div>');
+exit;
+?>
+
+// BASE INTEGRATION
+
+integration_server=bokeh-mysql-test
+integration_user=test
+integration_pwd=test
+integration_base=bokeh_test
+pwd_master=opac3
+
+// BASE OPAC3
diff --git a/tests/library/Class/Migration/141993_expected_config.ini b/tests/library/Class/Migration/141993_expected_config.ini
new file mode 100644
index 0000000000000000000000000000000000000000..c343bdb3d2647b28ce6bc42a155f629b5a1f59ed
--- /dev/null
+++ b/tests/library/Class/Migration/141993_expected_config.ini
@@ -0,0 +1,28 @@
+[global]
+timeZone = Europe/Paris
+locale = fr_FR.UTF-8
+
+; SMTP
+; mail.transport.smtp.host =
+
+; Used by global http client
+proxy.host = 
+proxy.port = 
+proxy.user = 
+proxy.pass =
+
+; SGBD
+sgbd.adapter = mysqli
+sgbd.config.host = bokeh-mysql-test
+sgbd.config.port = 3306
+sgbd.config.username = test
+sgbd.config.password = test
+sgbd.config.dbname = bokeh_test_db
+
+; Advanced
+amber.deploy = 0
+experimental_dev = true
+
+[local : global]				
+
+[production : global]
diff --git a/tests/library/Class/Migration/141993_expected_cosmogramme_config.php b/tests/library/Class/Migration/141993_expected_cosmogramme_config.php
new file mode 100644
index 0000000000000000000000000000000000000000..cafdf2c8bf7f708d338d3429f0918ba93a81216a
--- /dev/null
+++ b/tests/library/Class/Migration/141993_expected_cosmogramme_config.php
@@ -0,0 +1,16 @@
+<?php
+print("<center>");
+print("<br><br><br><br><br>");
+print('<div style="font-family:Verdana;font-size:20pt;color:red;font-weight:bold;">Accès interdit</div>');
+exit;
+?>
+
+// BASE INTEGRATION
+
+integration_server=bokeh-mysql-test
+integration_user=test
+integration_pwd=test
+integration_base=bokeh_test_db
+pwd_master=opac3
+
+// BASE OPAC3
diff --git a/tests/library/Class/Migration/UpdateConfigTest.php b/tests/library/Class/Migration/UpdateConfigTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..a24991e26b7a938332dd180c55e813982b8d9a25
--- /dev/null
+++ b/tests/library/Class/Migration/UpdateConfigTest.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class UpdateConfigFileTest extends ModelTestCase {
+  protected $_storm_default_to_volatile = true;
+
+
+  public function setUp() {
+    parent::setUp();
+
+    $file_system = $this
+      ->mock()->beStrict()
+
+      ->whenCalled('file_get_contents')->with(ROOT_PATH . 'config.ini')
+      ->answers((new Storm_FileSystem_Disk)->fileGetContents(__DIR__ . '/141993_config.ini'))
+
+      ->whenCalled('fopen')->with(ROOT_PATH . 'config.ini', 'w')
+      ->answers('123456')
+
+      ->whenCalled('fwrite')->with('123456', (new Storm_FileSystem_Disk)->fileGetContents(__DIR__ . '/141993_expected_config.ini'))
+      ->answers(true)
+
+      ->whenCalled('file_get_contents')->with(ROOT_PATH . 'cosmogramme/config.php')
+      ->answers((new Storm_FileSystem_Disk)->fileGetContents(__DIR__ . '/141993_cosmogramme_config.php'))
+
+      ->whenCalled('fopen')->with(ROOT_PATH . 'cosmogramme/config.php', 'w')
+      ->answers('654321')
+
+      ->whenCalled('fwrite')->with('654321', (new Storm_FileSystem_Disk)->fileGetContents(__DIR__ . '/141993_expected_cosmogramme_config.php'))
+      ->answers(true);
+
+    Class_Migration_UpdateConfig::setFileSystem($file_system);
+    Class_Migration_UpdateConfig::setEcho(true);
+
+    (new Class_Migration_UpdateConfig)->run('bokeh_test_db');
+  }
+
+
+  /** @test */
+  public function patchPathConstShouldNotContainsDoubleSlashes() {
+    $this->assertFalse(strpos(PATCH_PATH, '//'));
+  }
+}
diff --git a/tests/library/Class/Migration/UpdateDatabaseAfterSelectDbTest.php b/tests/library/Class/Migration/UpdateDatabaseAfterSelectDbTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..c144936745439f7d286823d16d0a600690fc8d58
--- /dev/null
+++ b/tests/library/Class/Migration/UpdateDatabaseAfterSelectDbTest.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class UpdateDatabaseAfterSelectDbTest extends ModelTestCase {
+  protected $_storm_default_to_volatile = true;
+
+
+  public function setUp() {
+    parent::setUp();
+    Class_CosmoVar::setValueOf('cache_path', './prod/cache/path/');
+    Class_CosmoVar::setValueOf('log_path', './prod/log/path/');
+    Class_CosmoVar::setValueOf('ftp_path', './prod/ftp/path/');
+    Class_CosmoVar::setValueOf('integration_path', './prod/integration/path/');
+    Class_CosmoVar::setValueOf('admin_login', 'prod_admin_login');
+    Class_CosmoVar::setValueOf('admin_pwd', 'prod_admin_pwd');
+
+    Class_AdminVar::set('ENABLE_COLLABORATIVE_BROWSING', '1');
+    Class_AdminVar::set('STATUS_REPORT_PUSH_URL', 'https://git-bokeh.org');
+    Class_AdminVar::set('FORCE_HTTPS', '1');
+
+    Class_Migration_UpdateDatabaseAfterSelectDb::setEcho(true);
+    (new Class_Migration_UpdateDatabaseAfterSelectDb)->run();
+  }
+
+
+  public function expectedCosmoVarValuesForKey() {
+    return [['./cosmogramme/fichiers/cache/', 'cache_path'],
+            ['./cosmogramme/fichiers/log/', 'log_path'],
+            ['./cosmogramme/fichiers/transferts/', 'ftp_path'],
+            ['./cosmogramme/fichiers/integration/', 'integration_path'],
+
+            ['admin', 'admin_login'],
+            ['admin', 'admin_pwd']
+    ];
+  }
+
+
+  /**
+	 * @test
+	 * @dataProvider expectedCosmoVarValuesForKey
+	 */
+  public function cosmoVarValuesShouldHaveChange($value, $key) {
+    $this->assertEquals($value, Class_CosmoVar::getValueOf($key));
+  }
+
+
+  public function expectedAdminVarValuesForKey() {
+    return [['0', 'ENABLE_COLLABORATIVE_BROWSING'],
+            ['no', 'STATUS_REPORT_PUSH_URL'],
+            ['0', 'FORCE_HTTPS']
+    ];
+  }
+
+
+  /**
+	 * @test
+	 * @dataProvider expectedAdminVarValuesForKey
+	 */
+  public function adminVarValuesShouldHaveChange($value, $key) {
+    $this->assertEquals($value, Class_AdminVar::get($key));
+  }
+}