docs: def deploy_schema_to_db(self, mode='safe', files_deployment=None, vcs_ref=None, vcs_link=None,
                              issue_ref=None, issue_link=None, compare_table_scripts_as_int=False,
                              config_path=None, config_dict=None, config_object=None, source_code_path=None,
                              auto_commit=False):
          """
          Deploys schema
          :param files_deployment: if specific script to be deployed, only find them
          :param mode:
          :param vcs_ref:
          :param vcs_link:
          :param issue_ref:
          :param issue_link:
          :param compare_table_scripts_as_int:
          :param config_path:
          :param config_dict:
          :param config_object:
          :param source_code_path:
          :param auto_commit:
          :return: dictionary of the following format:
              {
                  code: 0 if all fine, otherwise something else,
                  message: message on the output
                  function_scripts_requested: list of function files requested for deployment
                  function_scripts_deployed: list of function files deployed
                  type_scripts_requested: list of type files requested for deployment
                  type_scripts_deployed: list of type files deployed
                  view_scripts_requested: list of view files requested for deployment
                  view_scripts_deployed: list of view files deployed
                  trigger_scripts_requested: list of trigger files requested for deployment
                  trigger_scripts_deployed: list of trigger files deployed
                  table_scripts_requested: list of table files requested for deployment
                  table_scripts_deployed: list of table files deployed
                  requested_files_count: count of requested files to deploy
                  deployed_files_count: count of deployed files
              }
          :rtype: dict
          """
  
          return_value = {}
          if files_deployment:
              return_value['function_scripts_requested'] = files_deployment
              return_value['type_scripts_requested'] = []
              return_value['view_scripts_requested'] = []
              return_value['trigger_scripts_requested'] = []
              return_value['table_scripts_requested'] = []
  
          if auto_commit:
              if mode == 'safe' and files_deployment:
                  self._logger.debug("Auto commit mode is on. Be careful.")
              else:
                  self._logger.error("Auto commit deployment can only be done with file "
                                     "deployments and in safe mode for security reasons")
                  raise ValueError("Auto commit deployment can only be done with file "
                                   "deployments and in safe mode for security reasons")
  
          # set source code path if exists
          self._source_code_path = self._source_code_path or source_code_path
  
          # set configuration if either of config_path, config_dict, config_object are set.
          # Otherwise use configuration from class initialisation
          if config_object:
              self._config = config_object
          elif config_path or config_dict:
              self._config = pgpm.lib.utils.config.SchemaConfiguration(config_path, config_dict, self._source_code_path)
  
          # Check if in git repo
          if not vcs_ref:
              if pgpm.lib.utils.vcs.is_git_directory(self._source_code_path):
                  vcs_ref = pgpm.lib.utils.vcs.get_git_revision_hash(self._source_code_path)
                  self._logger.debug('commit reference to be deployed is {0}'.format(vcs_ref))
              else:
                  self._logger.debug('Folder is not a known vcs repository')
  
          self._logger.debug('Configuration of package {0} of version {1} loaded successfully.'
                             .format(self._config.name, self._config.version.raw))  # TODO: change to to_string once discussed
          # .format(self._config.name, self._config.version.to_string()))
  
          # Get scripts
          type_scripts_dict = self._get_scripts(self._config.types_path, files_deployment,
                                                "types", self._source_code_path)
          if not files_deployment:
              return_value['type_scripts_requested'] = [key for key in type_scripts_dict]
  
          function_scripts_dict = self._get_scripts(self._config.functions_path, files_deployment,
                                                    "functions", self._source_code_path)
          if not files_deployment:
              return_value['function_scripts_requested'] = [key for key in function_scripts_dict]
  
          view_scripts_dict = self._get_scripts(self._config.views_path, files_deployment,
                                                "views", self._source_code_path)
          if not files_deployment:
              return_value['view_scripts_requested'] = [key for key in view_scripts_dict]
  
          trigger_scripts_dict = self._get_scripts(self._config.triggers_path, files_deployment,
                                                   "triggers", self._source_code_path)
          if not files_deployment:
              return_value['trigger_scripts_requested'] = [key for key in trigger_scripts_dict]
  
          # before with table scripts only file name was an identifier. Now whole relative path the file
          # (relative to config.json)
          # table_scripts_dict_denormalised = self._get_scripts(self._config.tables_path, files_deployment,
          #                                                     "tables", self._source_code_path)
          # table_scripts_dict = {os.path.split(k)[1]: v for k, v in table_scripts_dict_denormalised.items()}
          table_scripts_dict = self._get_scripts(self._config.tables_path, files_deployment,
                                                 "tables", self._source_code_path)
          if not files_deployment:
              return_value['table_scripts_requested'] = [key for key in table_scripts_dict]
  
          if self._conn.closed:
              self._conn = psycopg2.connect(self._connection_string, connection_factory=pgpm.lib.utils.db.MegaConnection)
          cur = self._conn.cursor()
  
          # be cautious, dangerous thing
          if auto_commit:
              self._conn.autocommit = True
  
          # Check if DB is pgpm enabled
          if not pgpm.lib.utils.db.SqlScriptsHelper.schema_exists(cur, self._pgpm_schema_name):
              self._logger.error('Can\'t deploy schemas to DB where pgpm was not installed. '
                                 'First install pgpm by running pgpm install')
              self._conn.close()
              sys.exit(1)
  
          # check installed version of _pgpm schema.
          pgpm_v_db_tuple = pgpm.lib.utils.db.SqlScriptsHelper.get_pgpm_db_version(cur, self._pgpm_schema_name)
          pgpm_v_db = distutils.version.StrictVersion(".".join(pgpm_v_db_tuple))
          pgpm_v_script = distutils.version.StrictVersion(pgpm.lib.version.__version__)
          if pgpm_v_script > pgpm_v_db:
              self._logger.error('{0} schema version is outdated. Please run pgpm install --upgrade first.'
                                 .format(self._pgpm_schema_name))
              self._conn.close()
              sys.exit(1)
          elif pgpm_v_script < pgpm_v_db:
              self._logger.error('Deployment script\'s version is lower than the version of {0} schema '
                                 'installed in DB. Update pgpm script first.'.format(self._pgpm_schema_name))
              self._conn.close()
              sys.exit(1)
  
          # Resolve dependencies
          list_of_deps_ids = []
          if self._config.dependencies:
              _is_deps_resolved, list_of_deps_ids, _list_of_unresolved_deps = \
                  self._resolve_dependencies(cur, self._config.dependencies)
              if not _is_deps_resolved:
                  self._logger.error('There are unresolved dependencies. Deploy the following package(s) and try again:')
                  for unresolved_pkg in _list_of_unresolved_deps:
                      self._logger.error('{0}'.format(unresolved_pkg))
                  self._conn.close()
                  sys.exit(1)
  
          # Prepare and execute preamble
          _deployment_script_preamble = pkgutil.get_data('pgpm', 'lib/db_scripts/deploy_prepare_config.sql')
          self._logger.debug('Executing a preamble to deployment statement')
          cur.execute(_deployment_script_preamble)
  
          # Get schema name from project configuration
          schema_name = ''
          if self._config.scope == pgpm.lib.utils.config.SchemaConfiguration.SCHEMA_SCOPE:
              if self._config.subclass == 'versioned':
                  schema_name = '{0}_{1}'.format(self._config.name, self._config.version.raw)
  
                  self._logger.debug('Schema {0} will be updated'.format(schema_name))
              elif self._config.subclass == 'basic':
                  schema_name = '{0}'.format(self._config.name)
                  if not files_deployment:
                      self._logger.debug('Schema {0} will be created/replaced'.format(schema_name))
                  else:
                      self._logger.debug('Schema {0} will be updated'.format(schema_name))
  
          # Create schema or update it if exists (if not in production mode) and set search path
          if files_deployment:  # if specific scripts to be deployed
              if self._config.scope == pgpm.lib.utils.config.SchemaConfiguration.SCHEMA_SCOPE:
                  if not pgpm.lib.utils.db.SqlScriptsHelper.schema_exists(cur, schema_name):
                      self._logger.error('Can\'t deploy scripts to schema {0}. Schema doesn\'t exist in database'
                                         .format(schema_name))
                      self._conn.close()
                      sys.exit(1)
                  else:
                      pgpm.lib.utils.db.SqlScriptsHelper.set_search_path(cur, schema_name)
                      self._logger.debug('Search_path was changed to schema {0}'.format(schema_name))
          else:
              if self._config.scope == pgpm.lib.utils.config.SchemaConfiguration.SCHEMA_SCOPE:
                  if not pgpm.lib.utils.db.SqlScriptsHelper.schema_exists(cur, schema_name):
                      pgpm.lib.utils.db.SqlScriptsHelper.create_db_schema(cur, schema_name)
                  elif mode == 'safe':
                      self._logger.error('Schema already exists. It won\'t be overriden in safe mode. '
                                         'Rerun your script with "-m moderate", "-m overwrite" or "-m unsafe" flags')
                      self._conn.close()
                      sys.exit(1)
                  elif mode == 'moderate':
                      old_schema_exists = True
                      old_schema_rev = 0
                      while old_schema_exists:
                          old_schema_exists = pgpm.lib.utils.db.SqlScriptsHelper.schema_exists(
                              cur, schema_name + '_' + str(old_schema_rev))
                          if old_schema_exists:
                              old_schema_rev += 1
                      old_schema_name = schema_name + '_' + str(old_schema_rev)
                      self._logger.debug('Schema already exists. It will be renamed to {0} in moderate mode. Renaming...'
                                         .format(old_schema_name))
                      _rename_schema_script = "ALTER SCHEMA {0} RENAME TO {1};\n".format(schema_name, old_schema_name)
                      cur.execute(_rename_schema_script)
                      # Add metadata to pgpm schema
                      pgpm.lib.utils.db.SqlScriptsHelper.set_search_path(cur, self._pgpm_schema_name)
                      cur.callproc('_set_revision_package'.format(self._pgpm_schema_name),
                                   [self._config.name,
                                    self._config.subclass,
                                    old_schema_rev,
                                    self._config.version.major,
                                    self._config.version.minor,
                                    self._config.version.patch,
                                    self._config.version.pre])
                      self._logger.debug('Schema {0} was renamed to {1}. Meta info was added to {2} schema'
                                         .format(schema_name, old_schema_name, self._pgpm_schema_name))
                      pgpm.lib.utils.db.SqlScriptsHelper.create_db_schema(cur, schema_name)
                  elif mode == 'unsafe':
                      _drop_schema_script = "DROP SCHEMA {0} CASCADE;\n".format(schema_name)
                      cur.execute(_drop_schema_script)
                      self._logger.debug('Dropping old schema {0}'.format(schema_name))
                      pgpm.lib.utils.db.SqlScriptsHelper.create_db_schema(cur, schema_name)
  
          if self._config.scope == pgpm.lib.utils.config.SchemaConfiguration.SCHEMA_SCOPE:
              pgpm.lib.utils.db.SqlScriptsHelper.set_search_path(cur, schema_name)
  
          # Reordering and executing types
          return_value['type_scripts_deployed'] = []
          if len(type_scripts_dict) > 0:
              types_script = '\n'.join([''.join(value) for key, value in type_scripts_dict.items()])
              type_drop_scripts, type_ordered_scripts, type_unordered_scripts = self._reorder_types(types_script)
              if type_drop_scripts:
                  for statement in type_drop_scripts:
                      if statement:
                          cur.execute(statement)
              if type_ordered_scripts:
                  for statement in type_ordered_scripts:
                      if statement:
                          cur.execute(statement)
              if type_unordered_scripts:
                  for statement in type_unordered_scripts:
                      if statement:
                          cur.execute(statement)
              self._logger.debug('Types loaded to schema {0}'.format(schema_name))
              return_value['type_scripts_deployed'] = [key for key in type_scripts_dict]
          else:
              self._logger.debug('No type scripts to deploy')
  
          # Executing Table DDL scripts
          executed_table_scripts = []
          return_value['table_scripts_deployed'] = []
          if len(table_scripts_dict) > 0:
              if compare_table_scripts_as_int:
                  sorted_table_scripts_dict = collections.OrderedDict(sorted(table_scripts_dict.items(),
                                                                             key=lambda t: int(t[0].rsplit('.', 1)[0])))
              else:
                  sorted_table_scripts_dict = collections.OrderedDict(sorted(table_scripts_dict.items(),
                                                                             key=lambda t: t[0].rsplit('.', 1)[0]))
  
              self._logger.debug('Running Table DDL scripts')
              for key, value in sorted_table_scripts_dict.items():
                  pgpm.lib.utils.db.SqlScriptsHelper.set_search_path(cur, self._pgpm_schema_name)
                  cur.callproc('_is_table_ddl_executed'.format(self._pgpm_schema_name), [
                      key,
                      self._config.name,
                      self._config.subclass,
                      self._config.version.major,
                      self._config.version.minor,
                      self._config.version.patch,
                      self._config.version.pre
                  ])
                  is_table_executed = cur.fetchone()[0]
                  if self._config.scope == pgpm.lib.utils.config.SchemaConfiguration.SCHEMA_SCOPE:
                      pgpm.lib.utils.db.SqlScriptsHelper.set_search_path(cur, schema_name)
                  elif self._config.scope == pgpm.lib.utils.config.SchemaConfiguration.DATABASE_SCOPE:
                      cur.execute("SET search_path TO DEFAULT ;")
                  if (not is_table_executed) or (mode == 'unsafe'):
                      # if auto commit mode than every statement is called separately.
                      # this is done this way as auto commit is normally used when non transaction statements are called
                      # then this is needed to avoid "cannot be executed from a function or multi-command string" errors
                      if auto_commit:
                          for statement in sqlparse.split(value):
                              if statement:
                                  cur.execute(statement)
                      else:
                          cur.execute(value)
                      self._logger.debug(value)
                      self._logger.debug('{0} executed for schema {1}'.format(key, schema_name))
                      executed_table_scripts.append(key)
                      return_value['table_scripts_deployed'].append(key)
                  else:
                      self._logger.debug('{0} is not executed for schema {1} as it has already been executed before. '
                                         .format(key, schema_name))
          else:
              self._logger.debug('No Table DDL scripts to execute')
  
          # Executing functions
          return_value['function_scripts_deployed'] = []
          if len(function_scripts_dict) > 0:
              self._logger.debug('Running functions definitions scripts')
              for key, value in function_scripts_dict.items():
                  # if auto commit mode than every statement is called separately.
                  # this is done this way as auto commit is normally used when non transaction statements are called
                  # then this is needed to avoid "cannot be executed from a function or multi-command string" errors
                  if auto_commit:
                      for statement in sqlparse.split(value):
                          if statement:
                              cur.execute(statement)
                  else:
                      cur.execute(value)
                  return_value['function_scripts_deployed'].append(key)
              self._logger.debug('Functions loaded to schema {0}'.format(schema_name))
          else:
              self._logger.debug('No function scripts to deploy')
  
          # Executing views
          return_value['view_scripts_deployed'] = []
          if len(view_scripts_dict) > 0:
              self._logger.debug('Running views definitions scripts')
              for key, value in view_scripts_dict.items():
                  # if auto commit mode than every statement is called separately.
                  # this is done this way as auto commit is normally used when non transaction statements are called
                  # then this is needed to avoid "cannot be executed from a function or multi-command string" errors
                  if auto_commit:
                      for statement in sqlparse.split(value):
                          if statement:
                              cur.execute(statement)
                  else:
                      cur.execute(value)
                  return_value['view_scripts_deployed'].append(key)
              self._logger.debug('Views loaded to schema {0}'.format(schema_name))
          else:
              self._logger.debug('No view scripts to deploy')
  
          # Executing triggers
          return_value['trigger_scripts_deployed'] = []
          if len(trigger_scripts_dict) > 0:
              self._logger.debug('Running trigger definitions scripts')
              for key, value in trigger_scripts_dict.items():
                  # if auto commit mode than every statement is called separately.
                  # this is done this way as auto commit is normally used when non transaction statements are called
                  # then this is needed to avoid "cannot be executed from a function or multi-command string" errors
                  if auto_commit:
                      for statement in sqlparse.split(value):
                          if statement:
                              cur.execute(statement)
                  else:
                      cur.execute(value)
                  return_value['trigger_scripts_deployed'].append(key)
              self._logger.debug('Triggers loaded to schema {0}'.format(schema_name))
          else:
              self._logger.debug('No trigger scripts to deploy')
  
          # alter schema privileges if needed
          if (not files_deployment) and mode != 'overwrite' \
                  and self._config.scope == pgpm.lib.utils.config.SchemaConfiguration.SCHEMA_SCOPE:
              pgpm.lib.utils.db.SqlScriptsHelper.revoke_all(cur, schema_name, 'public')
              if self._config.usage_roles:
                  pgpm.lib.utils.db.SqlScriptsHelper.grant_usage_privileges(
                      cur, schema_name, ', '.join(self._config.usage_roles))
                  self._logger.debug('User(s) {0} was (were) granted usage permissions on schema {1}.'
                                     .format(", ".join(self._config.usage_roles), schema_name))
              if self._config.owner_role:
                  pgpm.lib.utils.db.SqlScriptsHelper.set_search_path(cur, self._pgpm_schema_name)
                  cur.callproc('_alter_schema_owner', [schema_name, self._config.owner_role])
                  self._logger.debug('Ownership of schema {0} and all its objects was changed and granted to user {1}.'
                                     .format(schema_name, self._config.owner_role))
  
          # Add metadata to pgpm schema
          pgpm.lib.utils.db.SqlScriptsHelper.set_search_path(cur, self._pgpm_schema_name)
          cur.callproc('_upsert_package_info'.format(self._pgpm_schema_name),
                       [self._config.name,
                        self._config.subclass,
                        self._config.version.major,
                        self._config.version.minor,
                        self._config.version.patch,
                        self._config.version.pre,
                        self._config.version.metadata,
                        self._config.description,
                        self._config.license,
                        list_of_deps_ids,
                        vcs_ref,
                        vcs_link,
                        issue_ref,
                        issue_link])
          self._logger.debug('Meta info about deployment was added to schema {0}'
                             .format(self._pgpm_schema_name))
          pgpm_package_id = cur.fetchone()[0]
          if len(table_scripts_dict) > 0:
              for key in executed_table_scripts:
                  cur.callproc('_log_table_evolution'.format(self._pgpm_schema_name), [key, pgpm_package_id])
  
          # Commit transaction
          self._conn.commit()
  
          self._conn.close()
  
          deployed_files_count = len(return_value['function_scripts_deployed']) + \
                                 len(return_value['type_scripts_deployed']) + \
                                 len(return_value['view_scripts_deployed']) + \
                                 len(return_value['trigger_scripts_deployed']) + \
                                 len(return_value['table_scripts_deployed'])
  
          requested_files_count = len(return_value['function_scripts_requested']) + \
                                  len(return_value['type_scripts_requested']) + \
                                  len(return_value['view_scripts_requested']) + \
                                  len(return_value['trigger_scripts_requested']) + \
                                  len(return_value['table_scripts_requested'])
  
          return_value['deployed_files_count'] = deployed_files_count
          return_value['requested_files_count'] = requested_files_count
          if deployed_files_count == requested_files_count:
              return_value['code'] = self.DEPLOYMENT_OUTPUT_CODE_OK
              return_value['message'] = 'OK'
          else:
              return_value['code'] = self.DEPLOYMENT_OUTPUT_CODE_NOT_ALL_DEPLOYED
              return_value['message'] = 'Not all requested files were deployed'
          return return_value