Exemplo n.º 1
0
    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')
Exemplo n.º 2
0
    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)
Exemplo n.º 3
0
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.'))
Exemplo n.º 4
0
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}"
            )
Exemplo n.º 5
0
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
Exemplo n.º 6
0
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,
    )
Exemplo n.º 7
0
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()
Exemplo n.º 8
0
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)
Exemplo n.º 9
0
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()
Exemplo n.º 10
0
Arquivo: ops.py Projeto: syyunn/edgedb
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
Exemplo n.º 11
0
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()
Exemplo n.º 12
0
 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
Exemplo n.º 13
0
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
Exemplo n.º 14
0
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
Exemplo n.º 15
0
    )

    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)
Exemplo n.º 16
0
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,
        )
Exemplo n.º 17
0
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)
Exemplo n.º 18
0
 def _err(cls, msg):
     return errors.ConfigurationError(
         f'invalid {cls.__name__.lower()!r} value: {msg}')
Exemplo n.º 19
0
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