예제 #1
0
    async def read_objtypes(self, schema, only_modules, exclude_modules):
        objtype_list = await datasources.schema.objtypes.fetch(
            self.connection,
            modules=only_modules,
            exclude_modules=exclude_modules)
        objtype_list = collections.OrderedDict(
            (sn.Name(row['name']), row) for row in objtype_list)

        basemap = {}

        for name, row in objtype_list.items():
            objtype = {
                'id':
                row['id'],
                'name':
                name,
                'is_abstract':
                row['is_abstract'],
                'is_final':
                row['is_final'],
                'view_type': (s_types.ViewType(row['view_type'])
                              if row['view_type'] else None),
                'expr':
                (s_expr.ExpressionText(row['expr']) if row['expr'] else None)
            }

            basemap[name] = row['bases'] or []

            schema, objtype = s_objtypes.ObjectType.create_in_schema(
                schema,
                id=objtype['id'],
                name=name,
                is_abstract=objtype['is_abstract'],
                is_final=objtype['is_final'],
                view_type=objtype['view_type'],
                expr=objtype['expr'])

        for objtype in schema.get_objects(type=s_objtypes.BaseObjectType):
            try:
                bases = basemap[objtype.get_name(schema)]
            except KeyError:
                pass
            else:
                schema = objtype.set_field_value(
                    schema, 'bases', [schema.get(b) for b in bases])

        derived = await datasources.schema.objtypes.fetch_derived(
            self.connection)

        for row in derived:
            attrs = dict(row)
            attrs['name'] = sn.SchemaName(attrs['name'])
            attrs['bases'] = [schema.get(b) for b in attrs['bases']]
            attrs['view_type'] = (s_types.ViewType(attrs['view_type'])
                                  if attrs['view_type'] else None)
            attrs['is_derived'] = True
            schema, objtype = s_objtypes.ObjectType.create_in_schema(
                schema, **attrs)

        return schema
예제 #2
0
파일: stmtctx.py 프로젝트: mcaramma/edgedb
def declare_view(expr: qlast.Base,
                 alias: str,
                 *,
                 fully_detached: bool = False,
                 temporary_scope: bool = True,
                 ctx: context.ContextLevel) -> irast.Set:

    with ctx.newscope(temporary=temporary_scope, fenced=True) as subctx:
        if not fully_detached:
            cached_view_set = ctx.expr_view_cache.get((expr, alias))
            # Detach the view namespace and record the prefix
            # in the parent statement's fence node.
            add_ns = (irast.WeakNamespace(ctx.aliases.get('ns')), )
            subctx.path_id_namespace = subctx.path_id_namespace + add_ns
            ctx.path_scope.namespaces.add(subctx.path_id_namespace[-1])
        else:
            cached_view_set = None

        if ctx.stmt is not None:
            subctx.stmt = ctx.stmt.parent_stmt

        if cached_view_set is not None:
            subctx.view_scls = cached_view_set.scls
            view_name = cached_view_set.scls.name
        else:
            if isinstance(alias, s_name.SchemaName):
                basename = alias
            else:
                basename = s_name.SchemaName(module='__view__', name=alias)

            view_name = s_name.SchemaName(
                module=ctx.derived_target_module or '_',
                name=s_obj.NamedObject.get_specialized_name(
                    basename, ctx.aliases.get('w')))

        subctx.toplevel_result_view_name = view_name

        view_set = dispatch.compile(astutils.ensure_qlstmt(expr), ctx=subctx)
        # The view path id _itself_ should not be in the nested namespace.
        view_set.path_id = view_set.path_id.replace_namespace(
            ctx.path_id_namespace)
        ctx.aliased_views[alias] = view_set.scls
        ctx.path_scope_map[view_set] = subctx.path_scope
        ctx.expr_view_cache[expr, alias] = view_set

    return view_set
예제 #3
0
def derive_view_name(
        scls: s_obj.Object,
        derived_name_quals: typing.Optional[typing.Sequence[str]]=(),
        derived_name_base: typing.Optional[str]=None, *,
        ctx: context.ContextLevel) -> sn.Name:

    if not derived_name_quals:
        derived_name_quals = (ctx.aliases.get('view'),)

    if not derived_name_base:
        derived_name_base = scls.shortname

    if ctx.derived_target_module:
        derived_name_module = ctx.derived_target_module
    else:
        derived_name_module = '__view__'

    derived_sname = scls.get_specialized_name(
        derived_name_base, *derived_name_quals)

    return sn.SchemaName(module=derived_name_module, name=derived_sname)
예제 #4
0
def derive_view(scls: s_obj.Object,
                source: typing.Optional[s_nodes.Node] = None,
                target: typing.Optional[s_nodes.Node] = None,
                *qualifiers,
                derived_name: typing.Optional[sn.SchemaName] = None,
                derived_name_quals: typing.Optional[typing.Sequence[str]] = (),
                is_insert: bool = False,
                is_update: bool = False,
                add_to_schema: bool = True,
                ctx: context.ContextLevel) -> s_obj.Object:
    if source is None:
        source = scls

    if derived_name is None:
        if not derived_name_quals:
            derived_name_quals = (ctx.aliases.get('view'), )

        if ctx.derived_target_module:
            derived_sname = scls.get_specialized_name(scls.shortname,
                                                      *derived_name_quals)

            derived_name = sn.SchemaName(module=ctx.derived_target_module,
                                         name=derived_sname)
        elif source is scls:
            derived_sname = scls.get_specialized_name(scls.shortname,
                                                      *derived_name_quals)

            derived_name = sn.SchemaName(module='__view__', name=derived_sname)

    if scls.generic():
        derived = scls.derive(ctx.schema,
                              source,
                              target,
                              *qualifiers,
                              name=derived_name,
                              mark_derived=True)
    else:
        # If this is already a derived class, reuse its name,
        # so that the correct storage relations are used in DML.
        if derived_name is None:
            derived_name = scls.name

        derived = scls.derive_copy(ctx.schema,
                                   source,
                                   target,
                                   *qualifiers,
                                   name=derived_name,
                                   attrs=dict(bases=[scls]),
                                   mark_derived=True)

        if isinstance(derived, s_sources.Source):
            for pn, ptr in derived.own_pointers.items():
                # This is a view of a view.  Make sure query-level
                # computable expressions for pointers are carried over.
                src_ptr = scls.pointers[pn]
                computable_data = ctx.source_map.get(src_ptr)
                if computable_data is not None:
                    ctx.source_map[ptr] = computable_data

    if isinstance(derived, s_types.Type):
        if is_insert:
            vtype = s_types.ViewType.Insert
        elif is_update:
            vtype = s_types.ViewType.Update
        else:
            vtype = s_types.ViewType.Select
        derived.view_type = vtype

    if (add_to_schema and not isinstance(derived, s_types.Collection)
            and ctx.schema.get(derived.name, None) is None):
        ctx.schema.add(derived)

    if isinstance(derived, s_types.Type):
        ctx.view_nodes[derived.name] = derived

    return derived
예제 #5
0
    async def read_objtypes(self, schema):
        tables = await introspection.tables.fetch_tables(
            self.connection, schema_pattern='edgedb%', table_pattern='%_data')
        tables = {(t['schema'], t['name']): t for t in tables}

        objtype_list = await datasources.schema.objtypes.fetch(self.connection)
        objtype_list = collections.OrderedDict(
            (sn.Name(row['name']), row) for row in objtype_list)

        visited_tables = set()

        self.table_cache.update({
            common.objtype_name_to_table_name(n, catenate=False): c
            for n, c in objtype_list.items()
        })

        basemap = {}

        for name, row in objtype_list.items():
            objtype = {
                'name':
                name,
                'title':
                self.json_to_word_combination(row['title']),
                'description':
                row['description'],
                'is_abstract':
                row['is_abstract'],
                'is_final':
                row['is_final'],
                'view_type': (s_types.ViewType(row['view_type'])
                              if row['view_type'] else None),
                'expr':
                (s_expr.ExpressionText(row['expr']) if row['expr'] else None)
            }

            table_name = common.objtype_name_to_table_name(name,
                                                           catenate=False)
            table = tables.get(table_name)

            if not table:
                msg = 'internal metadata incosistency'
                details = 'Record for type {!r} exists but ' \
                          'the table is missing'.format(name)
                raise s_err.SchemaError(msg, details=details)

            visited_tables.add(table_name)

            bases = await self.pg_table_inheritance_to_bases(
                table['name'], table['schema'], self.table_cache)

            basemap[name] = bases

            objtype = s_objtypes.ObjectType(name=name,
                                            title=objtype['title'],
                                            description=objtype['description'],
                                            is_abstract=objtype['is_abstract'],
                                            is_final=objtype['is_final'],
                                            view_type=objtype['view_type'],
                                            expr=objtype['expr'])

            schema.add(objtype)

        for objtype in schema.get_objects(type='ObjectType'):
            try:
                bases = basemap[objtype.name]
            except KeyError:
                pass
            else:
                objtype.bases = [schema.get(b) for b in bases]

        derived = await datasources.schema.objtypes.fetch_derived(
            self.connection)

        for row in derived:
            attrs = dict(row)
            attrs['name'] = sn.SchemaName(attrs['name'])
            attrs['bases'] = [schema.get(b) for b in attrs['bases']]
            attrs['view_type'] = (s_types.ViewType(attrs['view_type'])
                                  if attrs['view_type'] else None)
            attrs['is_derived'] = True
            objtype = s_objtypes.ObjectType(**attrs)
            schema.add(objtype)

        tabdiff = set(tables.keys()) - visited_tables
        if tabdiff:
            msg = 'internal metadata incosistency'
            details = 'Extraneous data tables exist: {}'.format(', '.join(
                '"%s.%s"' % t for t in tabdiff))
            raise s_err.SchemaError(msg, details=details)
예제 #6
0
파일: viewgen.py 프로젝트: 1st1/edgedb
def _normalize_view_ptr_expr(
        shape_el: qlast.ShapeElement,
        view_scls: s_nodes.Node,
        *,
        path_id: irast.PathId,
        is_insert: bool = False,
        is_update: bool = False,
        view_rptr: typing.Optional[context.ViewRPtr] = None,
        ctx: context.CompilerContext) -> s_pointers.Pointer:
    steps = shape_el.expr.steps
    is_linkprop = False
    is_mutation = is_insert or is_update
    # Pointers may be qualified by the explicit source
    # class, which is equivalent to Expr[IS Type].
    is_polymorphic = len(steps) == 2
    scls = view_scls.peel_view()
    ptrsource = scls
    qlexpr = None

    if is_polymorphic:
        source = qlast.TypeFilter(expr=qlast.Path(steps=[qlast.Source()]),
                                  type=qlast.TypeName(maintype=steps[0]))
        lexpr = steps[1]
        ptrsource = schemactx.get_schema_type(steps[0], ctx=ctx)
    elif len(steps) == 1:
        lexpr = steps[0]
        is_linkprop = lexpr.type == 'property'
        if is_linkprop:
            if view_rptr is None:
                raise errors.EdgeQLError(
                    'invalid reference to link property '
                    'in top level shape',
                    context=lexpr.context)
            if view_rptr.ptrcls is None:
                derive_ptrcls(view_rptr, target_scls=view_scls, ctx=ctx)
            ptrsource = scls = view_rptr.ptrcls
        source = qlast.Source()
    else:
        raise RuntimeError(
            f'unexpected path length in view shape: {len(steps)}')

    ptrname = (lexpr.ptr.module, lexpr.ptr.name)
    ptrcls_is_derived = False

    compexpr = shape_el.compexpr
    if compexpr is None and is_insert and shape_el.elements:
        # Nested insert short form:
        #     INSERT Foo { bar: Spam { name := 'name' }}
        # Expand to:
        #     INSERT Foo { bar := (INSERT Spam { name := 'name' }) }
        if lexpr.target is not None:
            ptr_target = schemactx.get_schema_type(lexpr.target, ctx=ctx)
        else:
            ptr_target = None

        base_ptrcls = ptrcls = setgen.resolve_ptr(
            ptrsource,
            ptrname,
            s_pointers.PointerDirection.Outbound,
            target=ptr_target,
            ctx=ctx)

        compexpr = qlast.InsertQuery(subject=qlast.Path(steps=[
            qlast.ObjectRef(name=ptrcls.target.name.name,
                            module=ptrcls.target.name.module)
        ]),
                                     shape=shape_el.elements)

    if compexpr is None:
        if lexpr.target is not None:
            ptr_target = schemactx.get_schema_type(lexpr.target, ctx=ctx)
        else:
            ptr_target = None

        base_ptrcls = ptrcls = setgen.resolve_ptr(
            ptrsource,
            ptrname,
            s_pointers.PointerDirection.Outbound,
            target=ptr_target,
            ctx=ctx)

        base_ptr_is_computable = ptrcls in ctx.source_map

        if ptr_target is not None and ptr_target != base_ptrcls.target:
            # This happens when a union type target is narrowed by an
            # [IS Type] construct.  Since the derived pointer will have
            # the correct target, we don't need to do anything, but
            # remove the [IS] qualifier to prevent recursion.
            lexpr.target = None
        else:
            ptr_target = ptrcls.target

        if ptrcls in ctx.pending_cardinality:
            # We do not know the parent's pointer cardinality yet.
            ptr_cardinality = None
        else:
            ptr_cardinality = ptrcls.cardinality

        if shape_el.elements:
            sub_view_rptr = context.ViewRPtr(
                ptrsource if is_linkprop else view_scls,
                ptrcls=ptrcls,
                is_insert=is_insert,
                is_update=is_update)

            sub_path_id = path_id.extend(ptrcls, target=ptrcls.target)
            ctx.path_scope.attach_path(sub_path_id)

            if is_update:
                for subel in shape_el.elements or []:
                    is_prop = (isinstance(subel.expr.steps[0], qlast.Ptr)
                               and subel.expr.steps[0].type == 'property')
                    if not is_prop:
                        raise errors.EdgeQLError(
                            'only references to link properties are allowed '
                            'in nested UPDATE shapes',
                            context=subel.context)

                ptr_target = _process_view(scls=ptr_target,
                                           path_id=sub_path_id,
                                           view_rptr=sub_view_rptr,
                                           elements=shape_el.elements,
                                           is_update=True,
                                           ctx=ctx)
            else:
                ptr_target = _process_view(scls=ptr_target,
                                           path_id=sub_path_id,
                                           view_rptr=sub_view_rptr,
                                           elements=shape_el.elements,
                                           ctx=ctx)

            ptrcls = sub_view_rptr.derived_ptrcls
            if ptrcls is None:
                ptrcls_is_derived = False
                ptrcls = sub_view_rptr.ptrcls
            else:
                ptrcls_is_derived = True

        if (shape_el.where or shape_el.orderby or shape_el.offset
                or shape_el.limit or base_ptr_is_computable or is_polymorphic):

            if qlexpr is None:
                qlexpr = qlast.Path(steps=[source, lexpr])

            qlexpr = astutils.ensure_qlstmt(qlexpr)
            qlexpr.where = shape_el.where
            qlexpr.orderby = shape_el.orderby
            qlexpr.offset = shape_el.offset
            qlexpr.limit = shape_el.limit
    else:
        try:
            base_ptrcls = ptrcls = setgen.resolve_ptr(
                ptrsource,
                ptrname,
                s_pointers.PointerDirection.Outbound,
                ctx=ctx)

            ptr_name = ptrcls.shortname
        except errors.EdgeQLReferenceError:
            if is_mutation:
                raise

            base_ptrcls = ptrcls = None

            ptr_module = (ptrname[0] or ctx.derived_target_module
                          or scls.name.module)

            ptr_name = sn.SchemaName(module=ptr_module, name=ptrname[1])

        qlexpr = astutils.ensure_qlstmt(compexpr)

        with ctx.newscope(fenced=True) as shape_expr_ctx:
            # Put current pointer class in context, so
            # that references to link properties in sub-SELECT
            # can be resolved.  This is necessary for proper
            # evaluation of link properties on computable links,
            # most importantly, in INSERT/UPDATE context.
            shape_expr_ctx.view_rptr = context.ViewRPtr(
                ptrsource if is_linkprop else view_scls,
                ptrcls=ptrcls,
                ptrcls_name=ptr_name,
                ptrcls_is_linkprop=is_linkprop,
                is_insert=is_insert,
                is_update=is_update)

            shape_expr_ctx.path_scope.unnest_fence = True

            if is_mutation:
                shape_expr_ctx.expr_exposed = True

            irexpr = dispatch.compile(qlexpr, ctx=shape_expr_ctx)

            irexpr.context = compexpr.context

            if base_ptrcls is None:
                base_ptrcls = ptrcls = shape_expr_ctx.view_rptr.ptrcls

            derived_ptrcls = shape_expr_ctx.view_rptr.derived_ptrcls
            if derived_ptrcls is not None:
                ptrcls_is_derived = True
                ptrcls = derived_ptrcls

        ptr_cardinality = None

        ptr_target = irutils.infer_type(irexpr, ctx.schema)
        if ptr_target is None:
            msg = 'cannot determine expression result type'
            raise errors.EdgeQLError(msg, context=shape_el.context)

        if is_mutation and not ptr_target.assignment_castable_to(
                base_ptrcls.target, schema=ctx.schema):
            # Validate that the insert/update expression is
            # of the correct class.
            lname = f'({ptrsource.name}).{ptrcls.shortname.name}'
            expected = [repr(str(base_ptrcls.target.name))]
            raise edgedb_error.InvalidPointerTargetError(
                f'invalid target for link {str(lname)!r}: '
                f'{str(ptr_target.name)!r} (expecting '
                f'{" or ".join(expected)})')

    if qlexpr is not None or ptr_target is not ptrcls.target:
        if not ptrcls_is_derived:
            if is_linkprop:
                rptrcls = view_rptr.derived_ptrcls
                if rptrcls is None:
                    rptrcls = derive_ptrcls(view_rptr,
                                            target_scls=view_scls,
                                            ctx=ctx)

                src_scls = rptrcls
            else:
                src_scls = view_scls

            ptrcls = schemactx.derive_view(ptrcls,
                                           src_scls,
                                           ptr_target,
                                           is_insert=is_insert,
                                           is_update=is_update,
                                           derived_name_quals=[view_scls.name],
                                           ctx=ctx)

        if qlexpr is not None:
            ctx.source_map[ptrcls] = (qlexpr, ctx)
            ptrcls.computable = True

    if not is_mutation:
        if ptr_cardinality is None:
            if compexpr is not None:
                ctx.pending_cardinality.add(ptrcls)
            elif ptrcls is not base_ptrcls:
                ctx.pointer_derivation_map[base_ptrcls].append(ptrcls)

            ptrcls.cardinality = None
        else:
            ptrcls.cardinality = ptr_cardinality

    if ptrcls.is_protected_pointer() and qlexpr is not None:
        msg = f'cannot assign to {ptrcls.shortname.name}'
        raise errors.EdgeQLError(msg, context=shape_el.context)

    return ptrcls