def coerce_value(self, setting: spec.Setting, *, allow_missing: bool = False): if issubclass(setting.type, types.ConfigType): try: return setting.type.from_pyvalue(self.value, allow_missing=allow_missing) except (ValueError, TypeError): raise errors.ConfigurationError( f'invalid value type for the {setting.name!r} setting') elif setting.set_of: if self.value is None and allow_missing: return None elif not typeutils.is_container(self.value): raise errors.ConfigurationError(f'invalid value type for the ' f'{setting.name!r} setting') else: for v in self.value: if not isinstance(v, setting.type): raise errors.ConfigurationError( f'invalid value type for the ' f'{setting.name!r} setting') return frozenset(self.value) else: if isinstance(self.value, setting.type): return self.value elif self.value is None and allow_missing: return None else: raise errors.ConfigurationError( f'invalid value type for the {setting.name!r} setting')
def _compile_ql_sess_state(self, ctx: CompileContext, ql: qlast.SetSessionState): current_tx = ctx.state.current_tx() schema = current_tx.get_schema() aliases = {} config_vals = {} for item in ql.items: if isinstance(item, qlast.SessionSettingModuleDecl): try: schema.get(item.module) except errors.InvalidReferenceError: raise errors.UnknownModuleError( f'module {item.module!r} does not exist') from None aliases[item.alias] = item.module elif isinstance(item, qlast.SessionSettingConfigDecl): name = item.alias try: desc = config.configs[name] except KeyError: raise errors.ConfigurationError( f'invalid SET expression: ' f'unknown CONFIG setting {name!r}') try: val_ir = ql_compiler.compile_ast_fragment_to_ir( item.expr, schema=schema) val = ireval.evaluate_to_python_val(val_ir.expr, schema=schema) except ireval.StaticEvaluationError: raise RuntimeError('invalid SET expression') else: if not isinstance(val, desc.type): dispname = val_ir.stype.get_displayname(schema) raise errors.ConfigurationError( f'expected a {desc.type.__name__} value, ' f'got {dispname!r}') else: config_vals[name] = val else: raise RuntimeError( f'unsupported SET command type {type(item)!r}') aliases = immutables.Map(aliases) config_vals = immutables.Map(config_vals) if aliases: ctx.state.current_tx().update_modaliases( ctx.state.current_tx().get_modaliases().update(aliases)) if config_vals: ctx.state.current_tx().update_config( ctx.state.current_tx().get_config().update(config_vals)) return dbstate.SessionStateQuery(sess_set_modaliases=aliases, sess_set_config=config_vals)
async def _check_data_dir_compatibility(conn): instancedata = await _get_instance_data(conn) datadir_version = instancedata.get('version') if datadir_version: datadir_major = datadir_version.get('major') expected_ver = buildmeta.get_version() if datadir_major != expected_ver.major: raise errors.ConfigurationError( 'database instance incompatible with this version of EdgeDB', details=(f'The database instance was initialized with ' f'EdgeDB version {datadir_major}, ' f'which is incompatible with this version ' f'{expected_ver.major}'), hint=(f'You need to recreate the instance and upgrade ' f'using dump/restore.')) datadir_catver = instancedata.get('catver') expected_catver = edbdef.EDGEDB_CATALOG_VERSION if datadir_catver != expected_catver: raise errors.ConfigurationError( 'database instance incompatible with this version of EdgeDB', details=(f'The database instance was initialized with ' f'EdgeDB format version {datadir_catver}, ' f'but this version of the server expects ' f'format version {expected_catver}'), hint=(f'You need to recreate the instance and upgrade ' f'using dump/restore.'))
def _check_capabilities(ctx: BootstrapContext) -> None: caps = ctx.cluster.get_runtime_params().instance_params.capabilities for cap in ctx.args.backend_capability_sets.must_be_present: if not caps & cap: raise errors.ConfigurationError( f"the backend doesn't have necessary capability: " f"{cap.name}" ) for cap in ctx.args.backend_capability_sets.must_be_absent: if caps & cap: raise errors.ConfigurationError( f"the backend was already bootstrapped with capability: " f"{cap.name}" )
def lookup( name: str, *configs: Mapping[str, SettingValue], allow_unrecognized: bool = False, spec: Optional[Spec] = None, ) -> Any: if spec is None: spec = get_settings() try: setting = spec[name] except (KeyError, TypeError): if allow_unrecognized: return None else: raise errors.ConfigurationError( f'unrecognized configuration parameter {name!r}') for c in configs: try: setting_value = c[name] except KeyError: pass else: return setting_value.value else: return setting.default
def compile_ConfigInsert( expr: qlast.ConfigInsert, *, ctx: context.ContextLevel) -> irast.Set: info = _validate_op(expr, ctx=ctx) if not expr.system: raise errors.UnsupportedFeatureError( f'CONFIGURE SESSION INSERT is not supported' ) level = 'SYSTEM' if expr.system else 'SESSION' subject = ctx.env.get_track_schema_object( f'cfg::{expr.name.name}', default=None) if subject is None: raise errors.ConfigurationError( f'{expr.name.name!r} is not a valid configuration item', context=expr.context, ) insert_stmt = qlast.InsertQuery( subject=qlast.Path( steps=[ qlast.ObjectRef( name=expr.name.name, module='cfg', ) ] ), shape=expr.shape, ) for el in expr.shape: if isinstance(el.compexpr, qlast.InsertQuery): _inject_tname(el.compexpr, ctx=ctx) with ctx.newscope() as subctx: subctx.expr_exposed = True subctx.modaliases = ctx.modaliases.copy() subctx.modaliases[None] = 'cfg' subctx.special_computables_in_mutation_shape |= {'_tname'} insert_ir = dispatch.compile(insert_stmt, ctx=subctx) insert_ir_set = setgen.ensure_set(insert_ir, ctx=subctx) assert isinstance(insert_ir_set.expr, irast.InsertStmt) insert_subject = insert_ir_set.expr.subject _validate_config_object(insert_subject, level=level, ctx=subctx) return setgen.ensure_set( irast.ConfigInsert( name=info.param_name, cardinality=info.cardinality, system=expr.system, requires_restart=info.requires_restart, backend_setting=info.backend_setting, expr=insert_subject, context=expr.context, ), ctx=ctx, )
def from_json(spec: spec.Spec, js: str) -> typing.Mapping: with immutables.Map().mutate() as mm: dct = json.loads(js) if not isinstance(dct, dict): raise errors.ConfigurationError( 'invalid JSON: top-level dict was expected') for key, value in dct.items(): setting = spec.get(key) if setting is None: raise errors.ConfigurationError( f'invalid JSON: unknown setting name {key!r}') mm[key] = value_from_json_value(setting, value) return mm.finish()
def compile_ConfigSet( expr: qlast.ConfigSet, *, ctx: context.ContextLevel, ) -> irast.Set: info = _validate_op(expr, ctx=ctx) param_val = dispatch.compile(expr.expr, ctx=ctx) param_type = info.param_type val_type = inference.infer_type(param_val, ctx.env) compatible = s_types.is_type_compatible(val_type, param_type, schema=ctx.env.schema) if not compatible: if not val_type.assignment_castable_to(param_type, ctx.env.schema): raise errors.ConfigurationError( f'invalid setting value type for {info.param_name}: ' f'{val_type.get_displayname(ctx.env.schema)!r} ' f'(expecting {param_type.get_displayname(ctx.env.schema)!r})') else: param_val = casts.compile_cast(param_val, param_type, srcctx=None, ctx=ctx) try: if expr.scope != qltypes.ConfigScope.GLOBAL: val = ireval.evaluate_to_python_val(param_val, schema=ctx.env.schema) else: val = None except ireval.UnsupportedExpressionError as e: raise errors.QueryError( f'non-constant expression in CONFIGURE {expr.scope} SET', context=expr.expr.context) from e else: if isinstance(val, statypes.ScalarType) and info.backend_setting: backend_expr = dispatch.compile( qlast.StringConstant.from_python(val.to_backend_str()), ctx=ctx, ) else: backend_expr = None config_set = irast.ConfigSet( name=info.param_name, cardinality=info.cardinality, required=info.required, scope=expr.scope, requires_restart=info.requires_restart, backend_setting=info.backend_setting, context=expr.context, expr=param_val, backend_expr=backend_expr, ) return setgen.ensure_set(config_set, ctx=ctx)
def from_json(spec: spec.Spec, js: str) -> SettingsMap: base: SettingsMap = immutables.Map() with base.mutate() as mm: dct = json.loads(js) if not isinstance(dct, dict): raise errors.ConfigurationError( 'invalid JSON: top-level dict was expected') for key, value in dct.items(): setting = spec.get(key) if setting is None: raise errors.ConfigurationError( f'invalid JSON: unknown setting name {key!r}') mm[key] = SettingValue( name=key, value=value_from_json_value(setting, value['value']), source=value['source'], ) return mm.finish()
def lookup(spec: spec.Spec, name: str, *configs: Mapping, allow_unrecognized: bool = False): try: setting = spec[name] except (KeyError, TypeError): if allow_unrecognized: return None else: raise errors.ConfigurationError( f'unrecognized configuration parameter {name!r}') for c in configs: try: return c[name] except KeyError: pass return setting.default
def from_dict( spec: spec.Spec, values: Mapping[str, Any], source: str, ) -> Mapping[str, SettingValue]: base: SettingsMap = immutables.Map() with base.mutate() as mm: for key, value in values.items(): setting = spec.get(key) if setting is None: raise errors.ConfigurationError( f'unknown setting name {key!r}') mm[key] = SettingValue( name=key, value=value_from_json_value(setting, value), source=source, ) return mm.finish()
def get_setting(self, spec: spec.Spec): try: return spec[self.setting_name] except KeyError: raise errors.ConfigurationError( f'unknown setting {self.setting_name!r}') from None
async def bootstrap(cluster, args) -> bool: pgconn = await cluster.connect() pgconn.add_log_listener(_pg_log_listener) std_schema = None try: membership = set() session_user = cluster.get_connection_params().user if session_user != edbdef.EDGEDB_SUPERUSER: membership.add(session_user) await _ensure_edgedb_role( cluster, pgconn, edbdef.EDGEDB_SUPERUSER, membership=membership, is_superuser=True, ) if session_user != edbdef.EDGEDB_SUPERUSER: await _execute( pgconn, f'SET ROLE {edbdef.EDGEDB_SUPERUSER};', ) cluster.set_default_session_authorization(edbdef.EDGEDB_SUPERUSER) need_meta_bootstrap = await _ensure_edgedb_template_database( cluster, pgconn) if need_meta_bootstrap: conn = await cluster.connect(database=edbdef.EDGEDB_TEMPLATE_DB) conn.add_log_listener(_pg_log_listener) await _execute( conn, f'ALTER SCHEMA public OWNER TO {edbdef.EDGEDB_SUPERUSER}', ) try: conn.add_log_listener(_pg_log_listener) await _ensure_meta_schema(conn) instancedata = await _populate_misc_instance_data(cluster) std_schema = await _init_stdlib(cluster, conn, testmode=args['testmode']) await _bootstrap_config_spec(std_schema, cluster) await _compile_sys_queries(std_schema, cluster) schema = await _init_defaults(std_schema, std_schema, conn) schema = await _populate_data(std_schema, schema, conn) await _configure(std_schema, conn, cluster, insecure=args['insecure'], testmode=args['testmode']) finally: await conn.close() await _ensure_edgedb_database(pgconn, edbdef.EDGEDB_SUPERUSER_DB, edbdef.EDGEDB_SUPERUSER, cluster=cluster) else: conn = await cluster.connect(database=edbdef.EDGEDB_SUPERUSER_DB) try: std_schema = await compiler.load_std_schema(conn) config_spec = config.load_spec_from_schema(std_schema) config.set_settings(config_spec) instancedata = await _get_instance_data(conn) finally: await conn.close() datadir_version = instancedata.get('version') if datadir_version: datadir_major = datadir_version.get('major') expected_ver = buildmeta.get_version() if datadir_major != expected_ver.major: raise errors.ConfigurationError( 'database instance incompatible with this version of EdgeDB', details=(f'The database instance was initialized with ' f'EdgeDB version {datadir_major}, ' f'which is incompatible with this version ' f'{expected_ver.major}'), hint=(f'You need to recreate the instance and upgrade ' f'using dump/restore.')) datadir_catver = instancedata.get('catver') expected_catver = edbdef.EDGEDB_CATALOG_VERSION if datadir_catver != expected_catver: raise errors.ConfigurationError( 'database instance incompatible with this version of EdgeDB', details=(f'The database instance was initialized with ' f'EdgeDB format version {datadir_catver}, ' f'but this version of the server expects ' f'format version {expected_catver}'), hint=(f'You need to recreate the instance and upgrade ' f'using dump/restore.')) await _ensure_edgedb_template_not_connectable(pgconn) await _ensure_edgedb_role( cluster, pgconn, args['default_database_user'], membership=membership, is_superuser=True, ) await _execute( pgconn, f"SET ROLE {args['default_database_user']};", ) await _ensure_edgedb_database( pgconn, args['default_database'], args['default_database_user'], cluster=cluster, ) finally: await pgconn.close() return need_meta_bootstrap
def _validate_op( expr: qlast.ConfigOp, *, ctx: context.ContextLevel) -> SettingInfo: if expr.name.module and expr.name.module != 'cfg': raise errors.QueryError( 'invalid configuration parameter name: module must be either ' '\'cfg\' or empty', context=expr.name.context, ) name = expr.name.name cfg_host_type = ctx.env.get_track_schema_type( sn.QualName('cfg', 'AbstractConfig')) assert isinstance(cfg_host_type, s_objtypes.ObjectType) cfg_type = None if isinstance(expr, (qlast.ConfigSet, qlast.ConfigReset)): # expr.name is the actual name of the property. ptr = cfg_host_type.maybe_get_ptr(ctx.env.schema, sn.UnqualName(name)) if ptr is not None: cfg_type = ptr.get_target(ctx.env.schema) if cfg_type is None: if isinstance(expr, qlast.ConfigSet): raise errors.ConfigurationError( f'unrecognized configuration parameter {name!r}', context=expr.context ) # expr.name is the name of the configuration type cfg_type = ctx.env.get_track_schema_type( sn.QualName('cfg', name), default=None) if cfg_type is None: raise errors.ConfigurationError( f'unrecognized configuration object {name!r}', context=expr.context ) assert isinstance(cfg_type, s_objtypes.ObjectType) ptr_candidate: Optional[s_pointers.Pointer] = None mro = [cfg_type] + list( cfg_type.get_ancestors(ctx.env.schema).objects(ctx.env.schema)) for ct in mro: ptrs = ctx.env.schema.get_referrers( ct, scls_type=s_links.Link, field_name='target') if ptrs: pointer_link = next(iter(ptrs)) assert isinstance(pointer_link, s_links.Link) ptr_candidate = pointer_link break if ( ptr_candidate is None or (ptr_source := ptr_candidate.get_source(ctx.env.schema)) is None or not ptr_source.issubclass(ctx.env.schema, cfg_host_type) ): raise errors.ConfigurationError( f'{name!r} cannot be configured directly' ) ptr = ptr_candidate name = ptr.get_shortname(ctx.env.schema).name
) backend_attr = ptr.get_annotations(ctx.env.schema).get( ctx.env.schema, sn.QualName('cfg', 'backend_setting'), None) if backend_attr is not None: backend_setting = json.loads(backend_attr.get_value(ctx.env.schema)) else: backend_setting = None compilation_attr = ptr.get_annotations(ctx.env.schema).get( ctx.env.schema, sn.QualName('cfg', 'affects_compilation'), None) if compilation_attr is not None: affects_compilation = ( json.loads(compilation_attr.get_value(ctx.env.schema))) else: affects_compilation = False if system and expr.scope is not qltypes.ConfigScope.INSTANCE: raise errors.ConfigurationError( f'{name!r} is a system-level configuration parameter; ' f'use "CONFIGURE INSTANCE"') return SettingInfo(param_name=name, param_type=cfg_type, cardinality=cardinality, requires_restart=requires_restart, backend_setting=backend_setting, affects_compilation=affects_compilation)
async def _bootstrap(ctx: BootstrapContext) -> None: args = ctx.args cluster = ctx.cluster backend_params = cluster.get_runtime_params() if backend_params.instance_params.version < edbdef.MIN_POSTGRES_VERSION: min_ver = '.'.join(str(v) for v in edbdef.MIN_POSTGRES_VERSION) raise errors.ConfigurationError( 'unsupported backend', details=( f'EdgeDB requires PostgreSQL version {min_ver} or later, ' f'while the specified backend reports itself as ' f'{backend_params.instance_params.version.string}.' ) ) if args.backend_capability_sets.must_be_absent: caps = backend_params.instance_params.capabilities disabled = [] for cap in args.backend_capability_sets.must_be_absent: if caps & cap: caps &= ~cap disabled.append(cap) if disabled: logger.info(f"the following backend capabilities are disabled: " f"{', '.join(str(cap.name) for cap in disabled)}") cluster.overwrite_capabilities(caps) _check_capabilities(ctx) if backend_params.has_create_role: superuser_uid = await _bootstrap_edgedb_super_roles(ctx) else: superuser_uid = uuidgen.uuid1mc() in_dev_mode = devmode.is_in_dev_mode() # Protect against multiple EdgeDB tenants from trying to bootstrap # on the same cluster in devmode, as that is both a waste of resources # and might result in broken stdlib cache. if in_dev_mode: bootstrap_lock = 0xEDB00001 await ctx.conn.execute('SELECT pg_advisory_lock($1)', bootstrap_lock) if backend_params.has_create_database: new_template_db_id = await _create_edgedb_template_database(ctx) tpl_db = cluster.get_db_name(edbdef.EDGEDB_TEMPLATE_DB) conn = await cluster.connect(database=tpl_db) else: new_template_db_id = uuidgen.uuid1mc() try: if backend_params.has_create_database: tpl_ctx = ctx._replace(conn=conn) conn.add_log_listener(_pg_log_listener) else: tpl_ctx = ctx await _populate_misc_instance_data(tpl_ctx) stdlib, config_spec, compiler = await _init_stdlib( tpl_ctx, testmode=args.testmode, global_ids={ edbdef.EDGEDB_SUPERUSER: superuser_uid, edbdef.EDGEDB_TEMPLATE_DB: new_template_db_id, } ) await _compile_sys_queries( tpl_ctx, stdlib.reflschema, compiler, config_spec, ) schema = s_schema.FlatSchema() schema = await _init_defaults(schema, compiler, tpl_ctx.conn) finally: if in_dev_mode: await ctx.conn.execute( 'SELECT pg_advisory_unlock($1)', bootstrap_lock, ) if backend_params.has_create_database: await conn.close() if backend_params.has_create_database: await _create_edgedb_database( ctx, edbdef.EDGEDB_SYSTEM_DB, edbdef.EDGEDB_SUPERUSER, builtin=True, ) conn = await cluster.connect( database=cluster.get_db_name(edbdef.EDGEDB_SYSTEM_DB)) try: conn.add_log_listener(_pg_log_listener) await _configure( ctx._replace(conn=conn), config_spec=config_spec, schema=schema, compiler=compiler, ) finally: await conn.close() else: await _configure( ctx, config_spec=config_spec, schema=schema, compiler=compiler, ) if backend_params.has_create_database: await _create_edgedb_database( ctx, edbdef.EDGEDB_SUPERUSER_DB, edbdef.EDGEDB_SUPERUSER, ) else: await _set_edgedb_database_metadata( ctx, edbdef.EDGEDB_SUPERUSER_DB, ) if ( backend_params.has_create_role and args.default_database_user and args.default_database_user != edbdef.EDGEDB_SUPERUSER ): await _ensure_edgedb_role( ctx, args.default_database_user, superuser=True, ) def_role = ctx.cluster.get_role_name(args.default_database_user) await _execute(ctx.conn, f"SET ROLE {qi(def_role)}") if ( backend_params.has_create_database and args.default_database and args.default_database != edbdef.EDGEDB_SUPERUSER_DB ): await _create_edgedb_database( ctx, args.default_database, args.default_database_user or edbdef.EDGEDB_SUPERUSER, )
def _validate_op( expr: qlast.ConfigOp, *, ctx: context.ContextLevel) -> SettingInfo: if expr.name.module and expr.name.module != 'cfg': raise errors.QueryError( 'invalid configuration parameter name: module must be either ' '\'cfg\' or empty', context=expr.name.context, ) name = expr.name.name cfg_host_type = ctx.env.get_track_schema_type('cfg::Config') assert isinstance(cfg_host_type, s_objtypes.ObjectType) cfg_type = None if isinstance(expr, (qlast.ConfigSet, qlast.ConfigReset)): # expr.name is the actual name of the property. ptr = cfg_host_type.getptr(ctx.env.schema, name) if ptr is not None: cfg_type = ptr.get_target(ctx.env.schema) if cfg_type is None: if isinstance(expr, qlast.ConfigSet): raise errors.ConfigurationError( f'unrecognized configuration parameter {name!r}', context=expr.context ) # expr.name is the name of the configuration type cfg_type = ctx.env.get_track_schema_type(f'cfg::{name}', default=None) if cfg_type is None: raise errors.ConfigurationError( f'unrecognized configuration object {name!r}', context=expr.context ) ptr_candidate: Optional[s_pointers.Pointer] = None mro = [cfg_type] + list( cfg_type.get_ancestors(ctx.env.schema).objects(ctx.env.schema)) for ct in mro: ptrs = ctx.env.schema.get_referrers( ct, scls_type=s_links.Link, field_name='target') if ptrs: ptr_candidate = next(iter(ptrs)) break if (ptr_candidate is None or ptr_candidate.get_source(ctx.env.schema) != cfg_host_type): raise errors.ConfigurationError( f'{name!r} cannot be configured directly' ) ptr = ptr_candidate name = ptr.get_shortname(ctx.env.schema).name assert isinstance(ptr, s_pointers.Pointer) sys_attr = ptr.get_annotations(ctx.env.schema).get( ctx.env.schema, 'cfg::system', None) system = ( sys_attr is not None and sys_attr.get_value(ctx.env.schema) == 'true' ) cardinality = ptr.get_cardinality(ctx.env.schema) assert cardinality is not None restart_attr = ptr.get_annotations(ctx.env.schema).get( ctx.env.schema, 'cfg::requires_restart', None) requires_restart = ( restart_attr is not None and restart_attr.get_value(ctx.env.schema) == 'true' ) backend_attr = ptr.get_annotations(ctx.env.schema).get( ctx.env.schema, 'cfg::backend_setting', None) if backend_attr is not None: backend_setting = json.loads(backend_attr.get_value(ctx.env.schema)) else: backend_setting = None if not expr.system and system: raise errors.ConfigurationError( f'{name!r} is a system-level configuration parameter; ' f'use "CONFIGURE SYSTEM"') return SettingInfo(param_name=name, param_type=cfg_type, cardinality=cardinality, requires_restart=requires_restart, backend_setting=backend_setting)
def _err(cls, msg): return errors.ConfigurationError( f'invalid {cls.__name__.lower()!r} value: {msg}')
async def _check_catalog_compatibility( ctx: BootstrapContext, ) -> asyncpg_con.Connection: tenant_id = ctx.cluster.get_runtime_params().instance_params.tenant_id is_default_tenant = tenant_id == buildmeta.get_default_tenant_id() if is_default_tenant: sys_db = await ctx.conn.fetchval( f''' SELECT datname FROM pg_database WHERE datname LIKE '%' || $1 ORDER BY datname = $1, datname DESC LIMIT 1 ''', edbdef.EDGEDB_SYSTEM_DB) else: sys_db = await ctx.conn.fetchval( f''' SELECT datname FROM pg_database WHERE datname = $1 ''', ctx.cluster.get_db_name(edbdef.EDGEDB_SYSTEM_DB)) if not sys_db: raise errors.ConfigurationError( 'database instance is corrupt', details=( f'The database instance does not appear to have been fully ' f'initialized or has been corrupted.')) conn = await ctx.cluster.connect(database=sys_db) try: instancedata = await _get_instance_data(conn) datadir_version = instancedata.get('version') if datadir_version: datadir_major = datadir_version.get('major') expected_ver = buildmeta.get_version() datadir_catver = instancedata.get('catver') expected_catver = edbdef.EDGEDB_CATALOG_VERSION status = dict( data_catalog_version=datadir_catver, expected_catalog_version=expected_catver, ) if datadir_major != expected_ver.major: if ctx.args.status_sink is not None: ctx.args.status_sink(f'INCOMPATIBLE={json.dumps(status)}') raise errors.ConfigurationError( 'database instance incompatible with this version of EdgeDB', details=(f'The database instance was initialized with ' f'EdgeDB version {datadir_major}, ' f'which is incompatible with this version ' f'{expected_ver.major}'), hint=(f'You need to recreate the instance and upgrade ' f'using dump/restore.')) if datadir_catver != expected_catver: if ctx.args.status_sink is not None: ctx.args.status_sink(f'INCOMPATIBLE={json.dumps(status)}') raise errors.ConfigurationError( 'database instance incompatible with this version of EdgeDB', details=(f'The database instance was initialized with ' f'EdgeDB format version {datadir_catver}, ' f'but this version of the server expects ' f'format version {expected_catver}'), hint=(f'You need to recreate the instance and upgrade ' f'using dump/restore.')) except Exception: await conn.close() raise return conn