You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

178 rivejä
6.3 KiB

  1. # Copyright 2014-2016 OpenMarket Ltd
  2. # Copyright 2020-2021 The Matrix.org Foundation C.I.C.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import argparse
  16. import logging
  17. import os
  18. from typing import Any, List
  19. from synapse.config._base import Config, ConfigError
  20. from synapse.types import JsonDict
  21. logger = logging.getLogger(__name__)
  22. NON_SQLITE_DATABASE_PATH_WARNING = """\
  23. Ignoring 'database_path' setting: not using a sqlite3 database.
  24. --------------------------------------------------------------------------------
  25. """
  26. DEFAULT_CONFIG = """\
  27. database:
  28. name: sqlite3
  29. args:
  30. database: %(database_path)s
  31. """
  32. class DatabaseConnectionConfig:
  33. """Contains the connection config for a particular database.
  34. Args:
  35. name: A label for the database, used for logging.
  36. db_config: The config for a particular database, as per `database`
  37. section of main config. Has three fields: `name` for database
  38. module name, `args` for the args to give to the database
  39. connector, and optional `data_stores` that is a list of stores to
  40. provision on this database (defaulting to all).
  41. """
  42. def __init__(self, name: str, db_config: dict):
  43. db_engine = db_config.get("name", "sqlite3")
  44. if db_engine not in ("sqlite3", "psycopg2"):
  45. raise ConfigError("Unsupported database type %r" % (db_engine,))
  46. if db_engine == "sqlite3":
  47. db_config.setdefault("args", {}).update(
  48. {"cp_min": 1, "cp_max": 1, "check_same_thread": False}
  49. )
  50. data_stores = db_config.get("data_stores")
  51. if data_stores is None:
  52. data_stores = ["main", "state"]
  53. self.name = name
  54. self.config = db_config
  55. # The `data_stores` config is actually talking about `databases` (we
  56. # changed the name).
  57. self.databases = data_stores
  58. class DatabaseConfig(Config):
  59. section = "database"
  60. def __init__(self, *args: Any):
  61. super().__init__(*args)
  62. self.databases: List[DatabaseConnectionConfig] = []
  63. def read_config(self, config: JsonDict, **kwargs: Any) -> None:
  64. # We *experimentally* support specifying multiple databases via the
  65. # `databases` key. This is a map from a label to database config in the
  66. # same format as the `database` config option, plus an extra
  67. # `data_stores` key to specify which data store goes where. For example:
  68. #
  69. # databases:
  70. # master:
  71. # name: psycopg2
  72. # data_stores: ["main"]
  73. # args: {}
  74. # state:
  75. # name: psycopg2
  76. # data_stores: ["state"]
  77. # args: {}
  78. multi_database_config = config.get("databases")
  79. database_config = config.get("database")
  80. database_path = config.get("database_path")
  81. if multi_database_config and database_config:
  82. raise ConfigError("Can't specify both 'database' and 'databases' in config")
  83. if multi_database_config:
  84. if database_path:
  85. raise ConfigError("Can't specify 'database_path' with 'databases'")
  86. self.databases = [
  87. DatabaseConnectionConfig(name, db_conf)
  88. for name, db_conf in multi_database_config.items()
  89. ]
  90. if database_config:
  91. self.databases = [DatabaseConnectionConfig("master", database_config)]
  92. if database_path:
  93. if self.databases and self.databases[0].name != "sqlite3":
  94. logger.warning(NON_SQLITE_DATABASE_PATH_WARNING)
  95. return
  96. database_config = {"name": "sqlite3", "args": {}}
  97. self.databases = [DatabaseConnectionConfig("master", database_config)]
  98. self.set_databasepath(database_path)
  99. def generate_config_section(self, data_dir_path: str, **kwargs: Any) -> str:
  100. return DEFAULT_CONFIG % {
  101. "database_path": os.path.join(data_dir_path, "homeserver.db")
  102. }
  103. def read_arguments(self, args: argparse.Namespace) -> None:
  104. """
  105. Cases for the cli input:
  106. - If no databases are configured and no database_path is set, raise.
  107. - No databases and only database_path available ==> sqlite3 db.
  108. - If there are multiple databases and a database_path raise an error.
  109. - If the database set in the config file is sqlite then
  110. overwrite with the command line argument.
  111. """
  112. if args.database_path is None:
  113. if not self.databases:
  114. raise ConfigError("No database config provided")
  115. return
  116. if len(self.databases) == 0:
  117. database_config = {"name": "sqlite3", "args": {}}
  118. self.databases = [DatabaseConnectionConfig("master", database_config)]
  119. self.set_databasepath(args.database_path)
  120. return
  121. if self.get_single_database().name == "sqlite3":
  122. self.set_databasepath(args.database_path)
  123. else:
  124. logger.warning(NON_SQLITE_DATABASE_PATH_WARNING)
  125. def set_databasepath(self, database_path: str) -> None:
  126. if database_path != ":memory:":
  127. database_path = self.abspath(database_path)
  128. self.databases[0].config["args"]["database"] = database_path
  129. @staticmethod
  130. def add_arguments(parser: argparse.ArgumentParser) -> None:
  131. db_group = parser.add_argument_group("database")
  132. db_group.add_argument(
  133. "-d",
  134. "--database-path",
  135. metavar="SQLITE_DATABASE_PATH",
  136. help="The path to a sqlite database to use.",
  137. )
  138. def get_single_database(self) -> DatabaseConnectionConfig:
  139. """Returns the database if there is only one, useful for e.g. tests"""
  140. if not self.databases:
  141. raise Exception("More than one database exists")
  142. return self.databases[0]