Ejemplo n.º 1
0
def init_stmt(
        irstmt: irast.Stmt, qlstmt: qlast.Statement, *,
        ctx: context.ContextLevel, parent_ctx: context.ContextLevel) -> None:

    ctx.stmt = irstmt
    if ctx.toplevel_stmt is None:
        parent_ctx.toplevel_stmt = ctx.toplevel_stmt = irstmt
        parent_ctx.path_scope = ctx.path_scope = irast.new_scope_tree()
    else:
        ctx.path_scope = parent_ctx.path_scope.attach_fence()

    pending_own_ns = parent_ctx.pending_stmt_own_path_id_namespace
    if pending_own_ns:
        ctx.path_scope.namespaces.add(pending_own_ns)

    pending_full_ns = parent_ctx.pending_stmt_full_path_id_namespace
    if pending_full_ns:
        ctx.path_id_namespace += tuple(pending_full_ns)

    metadata = ctx.stmt_metadata.get(qlstmt)
    if metadata is not None and metadata.is_unnest_fence:
        ctx.path_scope.unnest_fence = True

    irstmt.parent_stmt = parent_ctx.stmt

    process_with_block(qlstmt, ctx=ctx, parent_ctx=parent_ctx)
Ejemplo n.º 2
0
def init_context(
        *,
        schema: s_schema.Schema,
        func: typing.Optional[s_func.Function]=None,
        modaliases: typing.Optional[typing.Dict[str, str]]=None,
        anchors: typing.Optional[typing.Dict[str, s_obj.Object]]=None,
        singletons: typing.Optional[typing.Iterable[s_types.Type]]=None,
        security_context: typing.Optional[str]=None,
        derived_target_module: typing.Optional[str]=None,
        result_view_name: typing.Optional[str]=None,
        schema_view_mode: bool=False,
        implicit_id_in_shapes: bool=False) -> \
        context.ContextLevel:
    stack = context.CompilerContext()
    ctx = stack.current
    if not schema.get('__derived__', None):
        schema, _ = s_mod.Module.create_in_schema(schema, name='__derived__')
    ctx.env = context.Environment(schema=schema,
                                  path_scope=irast.new_scope_tree(),
                                  schema_view_mode=schema_view_mode)

    if singletons:
        # The caller wants us to treat these type references
        # as singletons for the purposes of the overall expression
        # cardinality inference, so we set up the scope tree in
        # the necessary fashion.
        for singleton in singletons:
            path_id = pathctx.get_path_id(singleton, ctx=ctx)
            ctx.env.path_scope.attach_path(path_id)

        ctx.path_scope = ctx.env.path_scope.attach_fence()

    if modaliases:
        ctx.modaliases.update(modaliases)

    if anchors:
        with ctx.newscope(fenced=True) as subctx:
            populate_anchors(anchors, ctx=subctx)

    ctx.func = func
    ctx.derived_target_module = derived_target_module
    ctx.toplevel_result_view_name = result_view_name
    ctx.implicit_id_in_shapes = implicit_id_in_shapes

    return ctx
Ejemplo n.º 3
0
def fuse_scope_branch(ir_set: irast.Set, parent: irast.ScopeTreeNode,
                      branch: irast.ScopeTreeNode, *,
                      ctx: context.ContextLevel) -> None:
    if parent.path_id is None:
        parent.attach_subtree(branch)
    else:
        if branch.path_id is None and len(branch.children) == 1:
            target_branch = next(iter(branch.children))
        else:
            target_branch = branch

        if parent.path_id == target_branch.path_id:
            new_root = irast.new_scope_tree()
            for child in tuple(target_branch.children):
                new_root.attach_child(child)

            parent.attach_subtree(new_root)
        else:
            parent.attach_subtree(branch)
Ejemplo n.º 4
0
    def __init__(self, prevlevel, mode):
        self.mode = mode

        if prevlevel is None:
            self.schema = None
            self.derived_target_module = None
            self.aliases = compiler.AliasGenerator()
            self.anchors = {}
            self.modaliases = {}
            self.arguments = {}
            self.all_sets = []
            self.stmt_metadata = {}
            self.completion_work = []
            self.pending_cardinality = set()
            self.pointer_derivation_map = collections.defaultdict(list)

            self.source_map = {}
            self.view_nodes = {}
            self.view_sets = {}
            self.aliased_views = collections.ChainMap()
            self.schema_view_cache = {}
            self.expr_view_cache = {}
            self.shape_type_cache = {}
            self.class_view_overrides = {}

            self.clause = None
            self.toplevel_clause = None
            self.toplevel_stmt = None
            self.stmt = None
            self.path_id_namespace = tuple()
            self.pending_stmt_own_path_id_namespace = None
            self.pending_stmt_full_path_id_namespace = None
            self.view_map = collections.ChainMap()
            self.class_shapes = collections.defaultdict(list)
            self.path_scope = None
            self.path_scope_is_temp = False
            self.path_scope_map = {}
            self.scope_id_ctr = compiler.Counter()
            self.in_aggregate = False
            self.view_scls = None
            self.expr_exposed = False

            self.partial_path_prefix = None

            self.view_rptr = None
            self.toplevel_result_view_name = None
            self.implicit_id_in_shapes = False
            self.empty_result_type_hint = None

        else:
            self.schema = prevlevel.schema
            self.derived_target_module = prevlevel.derived_target_module
            self.aliases = prevlevel.aliases
            self.arguments = prevlevel.arguments
            self.all_sets = prevlevel.all_sets
            self.stmt_metadata = prevlevel.stmt_metadata
            self.completion_work = prevlevel.completion_work
            self.pending_cardinality = prevlevel.pending_cardinality
            self.pointer_derivation_map = prevlevel.pointer_derivation_map

            self.source_map = prevlevel.source_map
            self.view_nodes = prevlevel.view_nodes
            self.view_sets = prevlevel.view_sets
            self.schema_view_cache = prevlevel.schema_view_cache
            self.expr_view_cache = prevlevel.expr_view_cache
            self.shape_type_cache = prevlevel.shape_type_cache

            self.path_id_namespace = prevlevel.path_id_namespace
            self.pending_stmt_own_path_id_namespace = \
                prevlevel.pending_stmt_own_path_id_namespace
            self.pending_stmt_full_path_id_namespace = \
                prevlevel.pending_stmt_full_path_id_namespace
            self.view_map = prevlevel.view_map
            self.class_shapes = prevlevel.class_shapes
            self.path_scope = prevlevel.path_scope
            self.path_scope_is_temp = prevlevel.path_scope_is_temp
            self.path_scope_map = prevlevel.path_scope_map
            self.scope_id_ctr = prevlevel.scope_id_ctr
            self.view_scls = prevlevel.view_scls
            self.expr_exposed = prevlevel.expr_exposed
            self.toplevel_clause = prevlevel.toplevel_clause
            self.toplevel_stmt = prevlevel.toplevel_stmt
            self.implicit_id_in_shapes = prevlevel.implicit_id_in_shapes
            self.empty_result_type_hint = prevlevel.empty_result_type_hint

            if mode == ContextSwitchMode.SUBQUERY:
                self.anchors = prevlevel.anchors.copy()
                self.modaliases = prevlevel.modaliases.copy()
                self.aliased_views = prevlevel.aliased_views.new_child()
                self.class_view_overrides = \
                    prevlevel.class_view_overrides.copy()

                self.pending_stmt_own_path_id_namespace = None
                self.pending_stmt_full_path_id_namespace = None

                self.view_rptr = None
                self.view_scls = None
                self.clause = None
                self.stmt = None
                self.in_aggregate = False

                self.partial_path_prefix = None

                self.view_rptr = None
                self.toplevel_result_view_name = None

            elif mode == ContextSwitchMode.DETACHED:
                self.anchors = prevlevel.anchors.copy()
                self.modaliases = prevlevel.modaliases.copy()
                self.aliased_views = collections.ChainMap()
                self.class_view_overrides = {}
                self.expr_exposed = False

                self.source_map = {}
                self.view_nodes = {}
                self.view_sets = {}
                self.path_id_namespace = (self.aliases.get('ns'), )
                self.pending_stmt_own_path_id_namespace = None
                self.pending_stmt_full_path_id_namespace = None

                self.view_rptr = None
                self.view_scls = None
                self.clause = None
                self.stmt = None
                self.in_aggregate = False

                self.partial_path_prefix = None

                self.view_rptr = None
                self.toplevel_result_view_name = None
            else:
                self.anchors = prevlevel.anchors
                self.modaliases = prevlevel.modaliases
                self.aliased_views = prevlevel.aliased_views
                self.class_view_overrides = prevlevel.class_view_overrides

                self.clause = prevlevel.clause
                self.stmt = prevlevel.stmt

                self.in_aggregate = prevlevel.in_aggregate

                self.partial_path_prefix = prevlevel.partial_path_prefix

                self.view_rptr = prevlevel.view_rptr
                self.toplevel_result_view_name = \
                    prevlevel.toplevel_result_view_name

            if mode in {
                    ContextSwitchMode.NEWFENCE_TEMP,
                    ContextSwitchMode.NEWSCOPE_TEMP
            }:
                if prevlevel.path_scope is None:
                    prevlevel.path_scope = irast.new_scope_tree()

                self.path_scope = prevlevel.path_scope.copy()
                self.path_scope_is_temp = True

            if mode in {
                    ContextSwitchMode.NEWFENCE, ContextSwitchMode.NEWFENCE_TEMP
            }:
                if prevlevel.path_scope is None:
                    prevlevel.path_scope = irast.new_scope_tree()

                self.path_scope = prevlevel.path_scope.attach_fence()

            if mode in {
                    ContextSwitchMode.NEWSCOPE, ContextSwitchMode.NEWSCOPE_TEMP
            }:
                if prevlevel.path_scope is None:
                    prevlevel.path_scope = irast.new_scope_tree()

                self.path_scope = prevlevel.path_scope.attach_branch()
Ejemplo n.º 5
0
    def _normalize_ptr_default(self, expr, source, ptr, ptrdecl):
        module_aliases = {None: source.name.module}

        ir, _, expr_text = edgeql.utils.normalize_tree(
            expr,
            self._schema,
            modaliases=module_aliases,
            anchors={qlast.Source: source})

        self_set = ast.find_children(
            ir,
            lambda n: getattr(n, 'anchor', None) == qlast.Source,
            terminate_early=True)

        try:
            expr_type = ir_utils.infer_type(ir, self._schema)
        except edgeql.EdgeQLError as e:
            raise s_err.SchemaError(
                'could not determine the result type of the default '
                'expression on {!s}.{!s}'.format(source.name, ptr.shortname),
                context=expr.context) from e

        ptr.default = expr_text
        ptr.normalize_defaults()

        if ptr.is_pure_computable():
            # Pure computable without explicit target.
            # Fixup pointer target and target property.
            ptr.target = expr_type

            if isinstance(ptr, s_links.Link):
                if not isinstance(expr_type, s_objtypes.ObjectType):
                    raise s_err.SchemaDefinitionError(
                        f'invalid link target, expected object type, got '
                        f'{expr_type.__class__.__name__}',
                        context=ptrdecl.expr.context)
            else:
                if not isinstance(expr_type,
                                  (s_scalars.ScalarType, s_types.Collection)):
                    raise s_err.SchemaDefinitionError(
                        f'invalid property target, expected primitive type, '
                        f'got {expr_type.__class__.__name__}',
                        context=ptrdecl.expr.context)

            if isinstance(ptr, s_links.Link):
                pname = s_name.Name('std::target')
                tgt_prop = ptr.pointers[pname]
                tgt_prop.target = expr_type

            cardinality = self._get_literal_attribute(ptrdecl, 'cardinality')
            if cardinality is not None:
                raise s_err.SchemaError(
                    'computable links must not define explicit cardinality',
                    context=expr.context)

            scope_tree_root = ir_ast.new_scope_tree()
            if self_set is not None:
                scope_tree_root.attach_path(self_set.path_id)
                scope_tree = scope_tree_root.attach_fence()
            else:
                scope_tree = scope_tree_root

            cardinality = \
                ir_inference.infer_cardinality(ir, scope_tree, self._schema)

            if cardinality == qlast.Cardinality.MANY:
                ptr.cardinality = s_pointers.PointerCardinality.ManyToMany
            else:
                ptr.cardinality = s_pointers.PointerCardinality.ManyToOne

        if (not isinstance(expr_type, s_types.Type) or
            (ptr.target is not None and not expr_type.issubclass(ptr.target))):
            raise s_err.SchemaError(
                'default value query must yield a single result of '
                'type {!r}'.format(ptr.target.name),
                context=expr.context)

        if not isinstance(ptr.target, s_scalars.ScalarType):
            many_mapping = (s_pointers.PointerCardinality.ManyToOne,
                            s_pointers.PointerCardinality.ManyToMany)
            if ptr.cardinality not in many_mapping:
                raise s_err.SchemaError(
                    'type links with query defaults '
                    'must have either a "*1" or "**" cardinality',
                    context=expr.context)
Ejemplo n.º 6
0
Archivo: links.py Proyecto: 1st1/edgedb
    def _cmd_tree_from_ast(cls, astnode, context, schema):
        from edb.lang.edgeql import utils as ql_utils
        from edb.lang.ir import ast as irast
        from edb.lang.ir import inference as ir_inference
        from edb.lang.ir import utils as ir_utils
        from . import objtypes as s_objtypes

        cmd = super()._cmd_tree_from_ast(astnode, context, schema)

        if isinstance(astnode, qlast.CreateConcreteLink):
            cmd.add(
                sd.AlterObjectProperty(property='required',
                                       new_value=astnode.is_required))

            # "source" attribute is set automatically as a refdict back-attr
            parent_ctx = context.get(LinkSourceCommandContext)
            source_name = parent_ctx.op.classname
            target_type = None

            if len(astnode.targets) > 1:
                cmd.add(
                    sd.AlterObjectProperty(
                        property='spectargets',
                        new_value=so.ObjectList([
                            utils.ast_to_typeref(t,
                                                 modaliases=context.modaliases,
                                                 schema=schema)
                            for t in astnode.targets
                        ])))

                target_name = sources.Source.gen_virt_parent_name(
                    (sn.Name(module=t.maintype.module, name=t.maintype.name)
                     for t in astnode.targets),
                    module=source_name.module)

                target = so.ObjectRef(classname=target_name)

                create_virt_parent = s_objtypes.CreateObjectType(
                    classname=target_name, metaclass=s_objtypes.ObjectType)

                create_virt_parent.update(
                    (sd.AlterObjectProperty(
                        property='bases',
                        new_value=so.ObjectList([
                            so.ObjectRef(
                                classname=sn.Name(module='std', name='Object'))
                        ])),
                     sd.AlterObjectProperty(property='name',
                                            new_value=target_name),
                     sd.AlterObjectProperty(property='is_virtual',
                                            new_value=True)))

                alter_db_ctx = context.get(s_db.DatabaseCommandContext)

                for cc in alter_db_ctx.op.get_subcommands(
                        type=s_objtypes.CreateObjectType):
                    if cc.classname == create_virt_parent.classname:
                        break
                else:
                    alter_db_ctx.op.add(create_virt_parent)
            else:
                target_expr = astnode.targets[0]
                if isinstance(target_expr, qlast.TypeName):
                    target = utils.ast_to_typeref(
                        target_expr,
                        modaliases=context.modaliases,
                        schema=schema)
                else:
                    # computable
                    source = schema.get(source_name, default=None)
                    if source is None:
                        raise s_err.SchemaDefinitionError(
                            f'cannot define link computables in CREATE TYPE',
                            hint='Perform a CREATE TYPE without the link '
                            'followed by ALTER TYPE defining the '
                            'computable',
                            context=target_expr.context)

                    ir, _, target_expr = ql_utils.normalize_tree(
                        target_expr, schema, anchors={qlast.Source: source})

                    try:
                        target_type = ir_utils.infer_type(ir, schema)
                    except edgeql.EdgeQLError as e:
                        raise s_err.SchemaDefinitionError(
                            'could not determine the result type of '
                            'computable expression',
                            context=target_expr.context) from e

                    target = utils.reduce_to_typeref(target_type)

                    cmd.add(
                        sd.AlterObjectProperty(property='default',
                                               new_value=target_expr))

                    cmd.add(
                        sd.AlterObjectProperty(property='computable',
                                               new_value=True))

                    scope_tree = irast.new_scope_tree()
                    scope_tree.attach_path(irast.PathId(source))
                    cardinality = ir_inference.infer_cardinality(
                        ir, scope_tree.attach_fence(), schema)

                    if cardinality == qlast.Cardinality.ONE:
                        link_card = pointers.PointerCardinality.ManyToOne
                    else:
                        link_card = pointers.PointerCardinality.ManyToMany

                    cmd.add(
                        sd.AlterObjectProperty(property='cardinality',
                                               new_value=link_card))

            if (isinstance(target, so.ObjectRef)
                    and target.classname == source_name):
                # Special case for loop links.  Since the target
                # is the same as the source, we know it's a proper
                # type.
                pass
            else:
                if target_type is None:
                    target_type = utils.resolve_typeref(target, schema=schema)

                if not isinstance(target_type, s_objtypes.ObjectType):
                    raise s_err.SchemaDefinitionError(
                        f'invalid link target, expected object type, got '
                        f'{target_type.__class__.__name__}',
                        context=astnode.targets[0].context)

            cmd.add(sd.AlterObjectProperty(property='target',
                                           new_value=target))

            base_prop_name = sn.Name('std::source')
            s_name = lproperties.Property.get_specialized_name(
                base_prop_name, cmd.classname)
            src_prop_name = sn.Name(name=s_name, module=cmd.classname.module)

            src_prop = lproperties.CreateProperty(
                classname=src_prop_name, metaclass=lproperties.Property)
            src_prop.update((
                sd.AlterObjectProperty(property='name',
                                       new_value=src_prop_name),
                sd.AlterObjectProperty(
                    property='bases',
                    new_value=[so.ObjectRef(classname=base_prop_name)]),
                sd.AlterObjectProperty(
                    property='source',
                    new_value=so.ObjectRef(classname=cmd.classname)),
                sd.AlterObjectProperty(
                    property='target',
                    new_value=so.ObjectRef(classname=source_name)),
                sd.AlterObjectProperty(property='required', new_value=True),
                sd.AlterObjectProperty(property='readonly', new_value=True),
            ))

            cmd.add(src_prop)

            base_prop_name = sn.Name('std::target')
            s_name = lproperties.Property.get_specialized_name(
                base_prop_name, cmd.classname)
            tgt_prop_name = sn.Name(name=s_name, module=cmd.classname.module)

            tgt_prop = lproperties.CreateProperty(
                classname=tgt_prop_name, metaclass=lproperties.Property)
            tgt_prop.update((
                sd.AlterObjectProperty(property='name',
                                       new_value=tgt_prop_name),
                sd.AlterObjectProperty(
                    property='bases',
                    new_value=[so.ObjectRef(classname=base_prop_name)]),
                sd.AlterObjectProperty(
                    property='source',
                    new_value=so.ObjectRef(classname=cmd.classname)),
                sd.AlterObjectProperty(property='target', new_value=target),
                sd.AlterObjectProperty(property='required', new_value=False),
                sd.AlterObjectProperty(property='readonly', new_value=True),
            ))

            cmd.add(tgt_prop)

            cls._parse_default(cmd)

        return cmd