server/serverctl.py
changeset 2493 9806571ea790
parent 2476 1294a6bdf3bf
child 2507 45248d0ad8a0
equal deleted inserted replaced
2492:c51be1cf8317 2493:9806571ea790
     3 :organization: Logilab
     3 :organization: Logilab
     4 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
     4 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     7 """
     7 """
     8 __docformat__ = "restructuredtext en"
     8 __docformat__ = 'restructuredtext en'
     9 
     9 
    10 import sys
    10 import sys
    11 import os
    11 import os
    12 
    12 
    13 from logilab.common.configuration import Configuration
    13 from logilab.common.configuration import Configuration
   167         config.write_sources_file(sourcescfg)
   167         config.write_sources_file(sourcescfg)
   168         # remember selected cubes for later initialization of the database
   168         # remember selected cubes for later initialization of the database
   169         config.write_bootstrap_cubes_file(cubes)
   169         config.write_bootstrap_cubes_file(cubes)
   170 
   170 
   171     def postcreate(self):
   171     def postcreate(self):
   172         if confirm('Do you want to run db-create to create the "system database" ?'):
   172         if confirm('Do you want to run db-create to create the system database ?'):
   173             verbosity = (self.config.mode == 'installed') and 'y' or 'n'
   173             verbosity = (self.config.mode == 'installed') and 'y' or 'n'
   174             cmd_run('db-create', self.config.appid, '--verbose=%s' % verbosity)
   174             cmd_run('db-create', self.config.appid, '--verbose=%s' % verbosity)
   175         else:
   175         else:
   176             print ('-> nevermind, you can do it later with '
   176             print ('-> nevermind, you can do it later with '
   177                    '"cubicweb-ctl db-create %s".' % self.config.appid)
   177                    '"cubicweb-ctl db-create %s".' % self.config.appid)
   244     """
   244     """
   245     name = 'db-create'
   245     name = 'db-create'
   246     arguments = '<instance>'
   246     arguments = '<instance>'
   247 
   247 
   248     options = (
   248     options = (
   249         ("create-db",
   249         ('create-db',
   250          {'short': 'c', 'type': "yn", 'metavar': '<y or n>',
   250          {'short': 'c', 'type': 'yn', 'metavar': '<y or n>',
   251           'default': True,
   251           'default': True,
   252           'help': 'create the database (yes by default)'}),
   252           'help': 'create the database (yes by default)'}),
   253         ("verbose",
   253         ('verbose',
   254          {'short': 'v', 'type' : 'yn', 'metavar': '<verbose>',
   254          {'short': 'v', 'type' : 'yn', 'metavar': '<verbose>',
   255           'default': 'n',
   255           'default': 'n',
   256           'help': 'verbose mode: will ask all possible configuration questions',
   256           'help': 'verbose mode: will ask all possible configuration questions',
   257           }
   257           }
   258          ),
   258          ),
   260     def run(self, args):
   260     def run(self, args):
   261         """run the command with its specific arguments"""
   261         """run the command with its specific arguments"""
   262         from logilab.common.adbh import get_adv_func_helper
   262         from logilab.common.adbh import get_adv_func_helper
   263         from indexer import get_indexer
   263         from indexer import get_indexer
   264         verbose = self.get('verbose')
   264         verbose = self.get('verbose')
   265         appid = pop_arg(args, msg="No instance specified !")
   265         appid = pop_arg(args, msg='No instance specified !')
   266         config = ServerConfiguration.config_for(appid)
   266         config = ServerConfiguration.config_for(appid)
   267         create_db = self.config.create_db
   267         create_db = self.config.create_db
   268         source = config.sources()['system']
   268         source = config.sources()['system']
   269         driver = source['db-driver']
   269         driver = source['db-driver']
   270         helper = get_adv_func_helper(driver)
   270         helper = get_adv_func_helper(driver)
   271         if create_db:
   271         if create_db:
   272             print '\n'+underline_title('Creating the "system database"')
   272             print '\n'+underline_title('Creating the system database')
   273             # connect on the dbms system base to create our base
   273             # connect on the dbms system base to create our base
   274             dbcnx = _db_sys_cnx(source, 'CREATE DATABASE and / or USER', verbose=verbose)
   274             dbcnx = _db_sys_cnx(source, 'CREATE DATABASE and / or USER', verbose=verbose)
   275             cursor = dbcnx.cursor()
   275             cursor = dbcnx.cursor()
   276             try:
   276             try:
   277                 if helper.users_support:
   277                 if helper.users_support:
   308                 helper.create_language(cursor, extlang)
   308                 helper.create_language(cursor, extlang)
   309         cursor.close()
   309         cursor.close()
   310         cnx.commit()
   310         cnx.commit()
   311         print '-> database for instance %s created and necessary extensions installed.' % appid
   311         print '-> database for instance %s created and necessary extensions installed.' % appid
   312         print
   312         print
   313         if confirm('Do you want to run db-init to initialize the "system database" ?'):
   313         if confirm('Do you want to run db-init to initialize the system database ?'):
   314             cmd_run('db-init', config.appid)
   314             cmd_run('db-init', config.appid)
   315         else:
   315         else:
   316             print ('-> nevermind, you can do it later with '
   316             print ('-> nevermind, you can do it later with '
   317                    '"cubicweb-ctl db-init %s".' % self.config.appid)
   317                    '"cubicweb-ctl db-init %s".' % self.config.appid)
   318 
   318 
   329     """
   329     """
   330     name = 'db-init'
   330     name = 'db-init'
   331     arguments = '<instance>'
   331     arguments = '<instance>'
   332 
   332 
   333     options = (
   333     options = (
   334         ("drop",
   334         ('drop',
   335          {'short': 'd', 'action': 'store_true',
   335          {'short': 'd', 'action': 'store_true',
   336           'default': False,
   336           'default': False,
   337           'help': 'insert drop statements to remove previously existant \
   337           'help': 'insert drop statements to remove previously existant \
   338 tables, indexes... (no by default)'}),
   338 tables, indexes... (no by default)'}),
   339         )
   339         )
   340 
   340 
   341     def run(self, args):
   341     def run(self, args):
   342         print '\n'+underline_title('Initializing the "system database"')
   342         print '\n'+underline_title('Initializing the system database')
   343         from cubicweb.server import init_repository
   343         from cubicweb.server import init_repository
   344         appid = pop_arg(args, msg="No instance specified !")
   344         appid = pop_arg(args, msg='No instance specified !')
   345         config = ServerConfiguration.config_for(appid)
   345         config = ServerConfiguration.config_for(appid)
   346         init_repository(config, drop=self.config.drop)
   346         init_repository(config, drop=self.config.drop)
   347 
   347 
   348 
   348 
   349 class GrantUserOnInstanceCommand(Command):
   349 class GrantUserOnInstanceCommand(Command):
   356     """
   356     """
   357     name = 'db-grant-user'
   357     name = 'db-grant-user'
   358     arguments = '<instance> <user>'
   358     arguments = '<instance> <user>'
   359 
   359 
   360     options = (
   360     options = (
   361         ("set-owner",
   361         ('set-owner',
   362          {'short': 'o', 'type' : "yn", 'metavar' : '<yes or no>',
   362          {'short': 'o', 'type' : 'yn', 'metavar' : '<yes or no>',
   363           'default' : False,
   363           'default' : False,
   364           'help': 'Set the user as tables owner if yes (no by default).'}
   364           'help': 'Set the user as tables owner if yes (no by default).'}
   365          ),
   365          ),
   366         )
   366         )
   367     def run(self, args):
   367     def run(self, args):
   368         """run the command with its specific arguments"""
   368         """run the command with its specific arguments"""
   369         from cubicweb.server.sqlutils import sqlexec, sqlgrants
   369         from cubicweb.server.sqlutils import sqlexec, sqlgrants
   370         appid = pop_arg(args, 1, msg="No instance specified !")
   370         appid = pop_arg(args, 1, msg='No instance specified !')
   371         user = pop_arg(args, msg="No user specified !")
   371         user = pop_arg(args, msg='No user specified !')
   372         config = ServerConfiguration.config_for(appid)
   372         config = ServerConfiguration.config_for(appid)
   373         source = config.sources()['system']
   373         source = config.sources()['system']
   374         set_owner = self.config.set_owner
   374         set_owner = self.config.set_owner
   375         cnx = system_source_cnx(source, special_privs='GRANT')
   375         cnx = system_source_cnx(source, special_privs='GRANT')
   376         cursor = cnx.cursor()
   376         cursor = cnx.cursor()
   398 
   398 
   399     def run(self, args):
   399     def run(self, args):
   400         """run the command with its specific arguments"""
   400         """run the command with its specific arguments"""
   401         from cubicweb.server.sqlutils import sqlexec, SQL_PREFIX
   401         from cubicweb.server.sqlutils import sqlexec, SQL_PREFIX
   402         from cubicweb.server.utils import crypt_password, manager_userpasswd
   402         from cubicweb.server.utils import crypt_password, manager_userpasswd
   403         appid = pop_arg(args, 1, msg="No instance specified !")
   403         appid = pop_arg(args, 1, msg='No instance specified !')
   404         config = ServerConfiguration.config_for(appid)
   404         config = ServerConfiguration.config_for(appid)
   405         sourcescfg = config.read_sources_file()
   405         sourcescfg = config.read_sources_file()
   406         try:
   406         try:
   407             adminlogin = sourcescfg['admin']['login']
   407             adminlogin = sourcescfg['admin']['login']
   408         except KeyError:
   408         except KeyError:
   442     """
   442     """
   443     name = 'start-repository'
   443     name = 'start-repository'
   444     arguments = '<instance>'
   444     arguments = '<instance>'
   445 
   445 
   446     options = (
   446     options = (
   447         ("debug",
   447         ('debug',
   448          {'short': 'D', 'action' : 'store_true',
   448          {'short': 'D', 'action' : 'store_true',
   449           'help': 'start server in debug mode.'}),
   449           'help': 'start server in debug mode.'}),
   450         )
   450         )
   451 
   451 
   452     def run(self, args):
   452     def run(self, args):
   453         from cubicweb.server.server import RepositoryServer
   453         from cubicweb.server.server import RepositoryServer
   454         appid = pop_arg(args, msg="No instance specified !")
   454         appid = pop_arg(args, msg='No instance specified !')
   455         config = ServerConfiguration.config_for(appid)
   455         config = ServerConfiguration.config_for(appid)
   456         debug = self.config.debug
   456         debug = self.config.debug
   457         # create the server
   457         # create the server
   458         server = RepositoryServer(config, debug)
   458         server = RepositoryServer(config, debug)
   459         # go ! (don't daemonize in debug mode)
   459         # go ! (don't daemonize in debug mode)
   471         server.connect(config['host'], 0)
   471         server.connect(config['host'], 0)
   472         server.run()
   472         server.run()
   473 
   473 
   474 
   474 
   475 def _remote_dump(host, appid, output, sudo=False):
   475 def _remote_dump(host, appid, output, sudo=False):
       
   476     # XXX generate unique/portable file name
   476     dmpcmd = 'cubicweb-ctl db-dump -o /tmp/%s.dump %s' % (appid, appid)
   477     dmpcmd = 'cubicweb-ctl db-dump -o /tmp/%s.dump %s' % (appid, appid)
   477     if sudo:
   478     if sudo:
   478         dmpcmd = 'sudo %s' % (dmpcmd)
   479         dmpcmd = 'sudo %s' % (dmpcmd)
   479     dmpcmd = 'ssh -t %s "%s"' % (host, dmpcmd)
   480     dmpcmd = 'ssh -t %s "%s"' % (host, dmpcmd)
   480     print dmpcmd
   481     print dmpcmd
   495         raise ExecutionError('Error while deleting remote dump')
   496         raise ExecutionError('Error while deleting remote dump')
   496 
   497 
   497 def _local_dump(appid, output):
   498 def _local_dump(appid, output):
   498     config = ServerConfiguration.config_for(appid)
   499     config = ServerConfiguration.config_for(appid)
   499     # schema=1 to avoid unnecessary schema loading
   500     # schema=1 to avoid unnecessary schema loading
   500     mih = config.migration_handler(connect=False, schema=1)
   501     mih = config.migration_handler(connect=False, schema=1, verbosity=1)
   501     mih.backup_database(output, askconfirm=False)
   502     mih.backup_database(output, askconfirm=False)
   502 
   503     mih.shutdown()
   503 def _local_restore(appid, backupfile, drop):
   504 
       
   505 def _local_restore(appid, backupfile, drop, systemonly=True):
   504     config = ServerConfiguration.config_for(appid)
   506     config = ServerConfiguration.config_for(appid)
       
   507     config.verbosity = 1 # else we won't be asked for confirmation on problems
   505     # schema=1 to avoid unnecessary schema loading
   508     # schema=1 to avoid unnecessary schema loading
   506     mih = config.migration_handler(connect=False, schema=1)
   509     mih = config.migration_handler(connect=False, schema=1, verbosity=1)
   507     mih.restore_database(backupfile, drop)
   510     mih.restore_database(backupfile, drop, systemonly, askconfirm=False)
   508     repo = mih.repo_connect()
   511     repo = mih.repo_connect()
   509     # version of the database
   512     # version of the database
   510     dbversions = repo.get_versions()
   513     dbversions = repo.get_versions()
   511     mih.shutdown()
   514     mih.shutdown()
   512     if not dbversions:
   515     if not dbversions:
   540         return 'needapplupgrade'
   543         return 'needapplupgrade'
   541     for cube in config.cubes():
   544     for cube in config.cubes():
   542         try:
   545         try:
   543             softversion = config.cube_version(cube)
   546             softversion = config.cube_version(cube)
   544         except ConfigurationError:
   547         except ConfigurationError:
   545             print "-> Error: no cube version information for %s, please check that the cube is installed." % cube
   548             print '-> Error: no cube version information for %s, please check that the cube is installed.' % cube
   546             continue
   549             continue
   547         try:
   550         try:
   548             applversion = vcconf[cube]
   551             applversion = vcconf[cube]
   549         except KeyError:
   552         except KeyError:
   550             print "-> Error: no cube version information for %s in version configuration." % cube
   553             print '-> Error: no cube version information for %s in version configuration.' % cube
   551             continue
   554             continue
   552         if softversion == applversion:
   555         if softversion == applversion:
   553             continue
   556             continue
   554         if softversion > applversion:
   557         if softversion > applversion:
   555             return 'needsoftupgrade'
   558             return 'needsoftupgrade'
   567     """
   570     """
   568     name = 'db-dump'
   571     name = 'db-dump'
   569     arguments = '<instance>'
   572     arguments = '<instance>'
   570 
   573 
   571     options = (
   574     options = (
   572         ("output",
   575         ('output',
   573          {'short': 'o', 'type' : "string", 'metavar' : '<file>',
   576          {'short': 'o', 'type' : 'string', 'metavar' : '<file>',
   574           'default' : None,
   577           'default' : None,
   575           'help': 'Specify the backup file where the backup will be stored.'}
   578           'help': 'Specify the backup file where the backup will be stored.'}
   576          ),
   579          ),
   577         ('sudo',
   580         ('sudo',
   578          {'short': 's', 'action' : 'store_true',
   581          {'short': 's', 'action' : 'store_true',
   580           'help': 'Use sudo on the remote host.'}
   583           'help': 'Use sudo on the remote host.'}
   581          ),
   584          ),
   582         )
   585         )
   583 
   586 
   584     def run(self, args):
   587     def run(self, args):
   585         appid = pop_arg(args, 1, msg="No instance specified !")
   588         appid = pop_arg(args, 1, msg='No instance specified !')
   586         if ':' in appid:
   589         if ':' in appid:
   587             host, appid = appid.split(':')
   590             host, appid = appid.split(':')
   588             _remote_dump(host, appid, self.config.output, self.config.sudo)
   591             _remote_dump(host, appid, self.config.output, self.config.sudo)
   589         else:
   592         else:
   590             _local_dump(appid, self.config.output)
   593             _local_dump(appid, self.config.output)
   598     """
   601     """
   599     name = 'db-restore'
   602     name = 'db-restore'
   600     arguments = '<instance> <backupfile>'
   603     arguments = '<instance> <backupfile>'
   601 
   604 
   602     options = (
   605     options = (
   603         ("no-drop",
   606         ('no-drop',
   604          {'short': 'n', 'action' : 'store_true',
   607          {'short': 'n', 'action' : 'store_true', 'default' : False,
   605           'default' : False,
       
   606           'help': 'for some reason the database doesn\'t exist and so '
   608           'help': 'for some reason the database doesn\'t exist and so '
   607           'should not be dropped.'}
   609           'should not be dropped.'}
   608          ),
   610          ),
       
   611         ('restore-all',
       
   612          {'short': 'r', 'action' : 'store_true', 'default' : False,
       
   613           'help': 'restore everything, eg not only the system source database '
       
   614           'but also data for all sources supporting backup/restore and custom '
       
   615           'instance data. In that case, <backupfile> is expected to be the '
       
   616           'timestamp of the backup to restore, not a file'}
       
   617          ),
   609         )
   618         )
   610 
   619 
   611     def run(self, args):
   620     def run(self, args):
   612         appid = pop_arg(args, 1, msg="No instance specified !")
   621         appid = pop_arg(args, 1, msg='No instance specified !')
   613         backupfile = pop_arg(args, msg="No backup file specified !")
   622         backupfile = pop_arg(args, msg='No backup file or timestamp specified !')
   614         _local_restore(appid, backupfile, not self.config.no_drop)
   623         _local_restore(appid, backupfile,
       
   624                        drop=not self.config.no_drop,
       
   625                        systemonly=not self.config.restore_all)
   615 
   626 
   616 
   627 
   617 class DBCopyCommand(Command):
   628 class DBCopyCommand(Command):
   618     """Copy the system database of an instance (backup and restore).
   629     """Copy the system database of an instance (backup and restore).
   619 
   630 
   626     """
   637     """
   627     name = 'db-copy'
   638     name = 'db-copy'
   628     arguments = '<src-instance> <dest-instance>'
   639     arguments = '<src-instance> <dest-instance>'
   629 
   640 
   630     options = (
   641     options = (
   631         ("no-drop",
   642         ('no-drop',
   632          {'short': 'n', 'action' : 'store_true',
   643          {'short': 'n', 'action' : 'store_true',
   633           'default' : False,
   644           'default' : False,
   634           'help': 'For some reason the database doesn\'t exist and so '
   645           'help': 'For some reason the database doesn\'t exist and so '
   635           'should not be dropped.'}
   646           'should not be dropped.'}
   636          ),
   647          ),
   637         ("keep-dump",
   648         ('keep-dump',
   638          {'short': 'k', 'action' : 'store_true',
   649          {'short': 'k', 'action' : 'store_true',
   639           'default' : False,
   650           'default' : False,
   640           'help': 'Specify that the dump file should not be automatically removed.'}
   651           'help': 'Specify that the dump file should not be automatically removed.'}
   641          ),
   652          ),
   642         ('sudo',
   653         ('sudo',
   646          ),
   657          ),
   647         )
   658         )
   648 
   659 
   649     def run(self, args):
   660     def run(self, args):
   650         import tempfile
   661         import tempfile
   651         srcappid = pop_arg(args, 1, msg="No source instance specified !")
   662         srcappid = pop_arg(args, 1, msg='No source instance specified !')
   652         destappid = pop_arg(args, msg="No destination instance specified !")
   663         destappid = pop_arg(args, msg='No destination instance specified !')
   653         _, output = tempfile.mkstemp()
   664         _, output = tempfile.mkstemp()
   654         if ':' in srcappid:
   665         if ':' in srcappid:
   655             host, srcappid = srcappid.split(':')
   666             host, srcappid = srcappid.split(':')
   656             _remote_dump(host, srcappid, output, self.config.sudo)
   667             _remote_dump(host, srcappid, output, self.config.sudo)
   657         else:
   668         else:
   671     """
   682     """
   672     name = 'db-check'
   683     name = 'db-check'
   673     arguments = '<instance>'
   684     arguments = '<instance>'
   674 
   685 
   675     options = (
   686     options = (
   676         ("checks",
   687         ('checks',
   677          {'short': 'c', 'type' : "csv", 'metavar' : '<check list>',
   688          {'short': 'c', 'type' : 'csv', 'metavar' : '<check list>',
   678           'default' : ('entities', 'relations', 'metadata', 'schema', 'text_index'),
   689           'default' : ('entities', 'relations', 'metadata', 'schema', 'text_index'),
   679           'help': 'Comma separated list of check to run. By default run all \
   690           'help': 'Comma separated list of check to run. By default run all \
   680 checks, i.e. entities, relations, text_index and metadata.'}
   691 checks, i.e. entities, relations, text_index and metadata.'}
   681          ),
   692          ),
   682 
   693 
   683         ("autofix",
   694         ('autofix',
   684          {'short': 'a', 'type' : "yn", 'metavar' : '<yes or no>',
   695          {'short': 'a', 'type' : 'yn', 'metavar' : '<yes or no>',
   685           'default' : False,
   696           'default' : False,
   686           'help': 'Automatically correct integrity problems if this option \
   697           'help': 'Automatically correct integrity problems if this option \
   687 is set to "y" or "yes", else only display them'}
   698 is set to "y" or "yes", else only display them'}
   688          ),
   699          ),
   689         ("reindex",
   700         ('reindex',
   690          {'short': 'r', 'type' : "yn", 'metavar' : '<yes or no>',
   701          {'short': 'r', 'type' : 'yn', 'metavar' : '<yes or no>',
   691           'default' : False,
   702           'default' : False,
   692           'help': 're-indexes the database for full text search if this \
   703           'help': 're-indexes the database for full text search if this \
   693 option is set to "y" or "yes" (may be long for large database).'}
   704 option is set to "y" or "yes" (may be long for large database).'}
   694          ),
   705          ),
   695         ("force",
   706         ('force',
   696          {'short': 'f', 'action' : "store_true",
   707          {'short': 'f', 'action' : 'store_true',
   697           'default' : False,
   708           'default' : False,
   698           'help': 'don\'t check instance is up to date.'}
   709           'help': 'don\'t check instance is up to date.'}
   699          ),
   710          ),
   700 
   711 
   701         )
   712         )
   702 
   713 
   703     def run(self, args):
   714     def run(self, args):
   704         from cubicweb.server.checkintegrity import check
   715         from cubicweb.server.checkintegrity import check
   705         appid = pop_arg(args, 1, msg="No instance specified !")
   716         appid = pop_arg(args, 1, msg='No instance specified !')
   706         config = ServerConfiguration.config_for(appid)
   717         config = ServerConfiguration.config_for(appid)
   707         config.repairing = self.config.force
   718         config.repairing = self.config.force
   708         repo, cnx = repo_cnx(config)
   719         repo, cnx = repo_cnx(config)
   709         check(repo, cnx,
   720         check(repo, cnx,
   710               self.config.checks, self.config.reindex, self.config.autofix)
   721               self.config.checks, self.config.reindex, self.config.autofix)
   721 
   732 
   722     options = ()
   733     options = ()
   723 
   734 
   724     def run(self, args):
   735     def run(self, args):
   725         from cubicweb.server.checkintegrity import reindex_entities
   736         from cubicweb.server.checkintegrity import reindex_entities
   726         appid = pop_arg(args, 1, msg="No instance specified !")
   737         appid = pop_arg(args, 1, msg='No instance specified !')
   727         config = ServerConfiguration.config_for(appid)
   738         config = ServerConfiguration.config_for(appid)
   728         repo, cnx = repo_cnx(config)
   739         repo, cnx = repo_cnx(config)
   729         session = repo._get_session(cnx.sessionid, setpool=True)
   740         session = repo._get_session(cnx.sessionid, setpool=True)
   730         reindex_entities(repo.schema, session)
   741         reindex_entities(repo.schema, session)
   731         cnx.commit()
   742         cnx.commit()
   742     """
   753     """
   743     name = 'schema-sync'
   754     name = 'schema-sync'
   744     arguments = '<instance>'
   755     arguments = '<instance>'
   745 
   756 
   746     def run(self, args):
   757     def run(self, args):
   747         appid = pop_arg(args, msg="No instance specified !")
   758         appid = pop_arg(args, msg='No instance specified !')
   748         config = ServerConfiguration.config_for(appid)
   759         config = ServerConfiguration.config_for(appid)
   749         mih = config.migration_handler()
   760         mih = config.migration_handler()
   750         mih.cmd_synchronize_schema()
   761         mih.cmd_synchronize_schema()
   751 
   762 
   752 
   763