Example #1
0
def amend_empty_set_type(es: irast.EmptySet, t: s_obj.Object, schema) -> None:
    alias = es.path_id[-1].name.name
    scls_name = s_name.Name(module='__expr__', name=alias)
    scls = t.__class__(name=scls_name, bases=[t])
    scls.acquire_ancestor_inheritance(schema)
    es.path_id = irast.PathId(scls)
    es.scls = t
Example #2
0
def resolve_schema_name(name: str, module: str, *,
                        ctx: context.ContextLevel) -> sn.Name:
    schema_module = ctx.modaliases.get(module)
    if schema_module is None:
        return None
    else:
        return sn.Name(name=name, module=schema_module)
Example #3
0
 def __init__(self, source, target, *, optional, cardinality):
     name = 'optindirection' if optional else 'indirection'
     super().__init__(name=s_name.Name(module='__type__', name=name),
                      source=source,
                      target=target,
                      direction=s_pointers.PointerDirection.Outbound)
     self.optional = optional
     self.cardinality = cardinality
Example #4
0
def compile_GroupQuery(
        expr: qlast.Base, *, ctx: context.ContextLevel) -> irast.Base:
    with ctx.subquery() as ictx:
        stmt = irast.GroupStmt()
        init_stmt(stmt, expr, ctx=ictx, parent_ctx=ctx)

        c = s_objtypes.ObjectType(
            name=s_name.Name(
                module='__group__', name=ctx.aliases.get('Group')),
            bases=[ctx.schema.get('std::Object')]
        )
        c.acquire_ancestor_inheritance(ctx.schema)

        stmt.group_path_id = pathctx.get_path_id(c, ctx=ictx)
        pathctx.register_set_in_scope(stmt.group_path_id, ctx=ictx)

        with ictx.newscope(fenced=True) as subjctx:
            subjctx.clause = 'input'

            subject_set = setgen.scoped_set(
                dispatch.compile(expr.subject, ctx=subjctx), ctx=subjctx)

            alias = expr.subject_alias or subject_set.path_id[0].name
            stmt.subject = stmtctx.declare_inline_view(
                subject_set, alias, ctx=ictx)

            with subjctx.new() as grpctx:
                stmt.groupby = compile_groupby_clause(
                    expr.groupby, singletons=grpctx.singletons, ctx=grpctx)

        with ictx.subquery() as isctx, isctx.newscope(fenced=True) as sctx:
            o_stmt = sctx.stmt = irast.SelectStmt()

            o_stmt.result = compile_result_clause(
                expr.result,
                view_scls=ctx.view_scls,
                view_rptr=ctx.view_rptr,
                result_alias=expr.result_alias,
                view_name=ctx.toplevel_result_view_name,
                ctx=sctx)

            o_stmt.where = clauses.compile_where_clause(
                expr.where, ctx=sctx)

            o_stmt.orderby = clauses.compile_orderby_clause(
                expr.orderby, ctx=sctx)

            o_stmt.offset = clauses.compile_limit_offset_clause(
                expr.offset, ctx=sctx)

            o_stmt.limit = clauses.compile_limit_offset_clause(
                expr.limit, ctx=sctx)

            stmt.result = setgen.scoped_set(o_stmt, ctx=sctx)

        result = fini_stmt(stmt, expr, ctx=ictx, parent_ctx=ctx)

    return result
Example #5
0
def new_empty_set(schema, *, scls=None, alias):
    if scls is None:
        base_scls = schema.get('std::str')
    else:
        base_scls = scls
    cls_name = s_name.Name(module='__expr__', name=alias)
    cls = base_scls.__class__(name=cls_name, bases=[base_scls])
    cls.acquire_ancestor_inheritance(schema)
    return irast.EmptySet(path_id=irast.PathId(cls), scls=scls)
Example #6
0
def get_expression_path_id(t: s_types.Type, alias: str, *,
                           ctx: context.ContextLevel) -> irast.PathId:
    cls_name = s_name.Name(module='__expr__', name=alias)
    if isinstance(t, (s_types.Collection, s_types.Tuple)):
        et = t.copy()
        et.name = cls_name
    else:
        et = t.__class__(name=cls_name, bases=[t])
        et.acquire_ancestor_inheritance(ctx.schema)
    return pathctx.get_path_id(et, ctx=ctx)
Example #7
0
def get_schema_object(
        name: typing.Union[str, qlast.ObjectRef],
        module: typing.Optional[str] = None,
        *,
        item_types: typing.Optional[typing.List[s_obj.ObjectMeta]],
        ctx: context.ContextLevel,
        srcctx: typing.Optional[parsing.ParserContext] = None) -> s_obj.Object:

    if isinstance(name, qlast.ObjectRef):
        if srcctx is None:
            srcctx = name.context
        module = name.module
        name = name.name

    if module:
        name = sn.Name(name=name, module=module)

    if not module:
        result = ctx.aliased_views.get(name)
        if result is not None:
            return result

    try:
        scls = ctx.schema.get(name=name,
                              module_aliases=ctx.modaliases,
                              type=item_types)

    except s_err.ItemNotFoundError as e:
        qlerror = qlerrors.EdgeQLError(e.args[0], context=srcctx)
        s_utils.enrich_schema_lookup_error(qlerror,
                                           name,
                                           modaliases=ctx.modaliases,
                                           schema=ctx.schema,
                                           item_types=item_types)

        raise qlerror

    except s_err.SchemaError as e:
        raise qlerrors.EdgeQLError(e.args[0], context=srcctx)

    result = ctx.aliased_views.get(scls.name)
    if result is None:
        result = scls

    return result
Example #8
0
def compile_FunctionCall(expr: qlast.Base, *,
                         ctx: context.ContextLevel) -> irast.Base:
    with ctx.new() as fctx:
        if isinstance(expr.func, str):
            funcname = expr.func
        else:
            funcname = sn.Name(expr.func[1], expr.func[0])

        funcs = fctx.schema.get_functions(funcname,
                                          module_aliases=fctx.modaliases)

        if funcs is None:
            raise errors.EdgeQLError(
                f'could not resolve function name {funcname}',
                context=expr.context)

        fctx.in_func_call = True
        args, kwargs, arg_types = process_func_args(expr, funcname, ctx=fctx)

        for funcobj in funcs:
            if check_function(funcobj, arg_types):
                break
        else:
            raise errors.EdgeQLError(
                f'could not find a function variant {funcname}',
                context=expr.context)

        fixup_param_scope(funcobj, args, kwargs, ctx=fctx)

        node = irast.FunctionCall(func=funcobj, args=args, kwargs=kwargs)

        if funcobj.initial_value is not None:
            rtype = irutils.infer_type(node, fctx.schema)
            iv_ql = qlast.TypeCast(expr=qlparser.parse_fragment(
                funcobj.initial_value),
                                   type=typegen.type_to_ql_typeref(rtype))
            node.initial_value = dispatch.compile(iv_ql, ctx=fctx)

    ir_set = setgen.ensure_set(node, ctx=ctx)
    return ir_set
Example #9
0
def _get_link_view(mcls, schema_cls, field, ptr, refdict, schema):
    pn = ptr.shortname

    if refdict:
        if (issubclass(mcls, s_inheriting.InheritingObject)
                or mcls is s_named.NamedObject):

            if mcls is s_named.NamedObject:
                schematab = 'edgedb.InheritingObject'
            else:
                schematab = 'edgedb.{}'.format(mcls.__name__)

            link_query = '''
                SELECT DISTINCT ON ((cls.id, r.bases[1]))
                    cls.id  AS {src},
                    r.id    AS {tgt}
                FROM
                    (SELECT
                        s.id                AS id,
                        ancestry.ancestor   AS ancestor,
                        ancestry.depth      AS depth
                     FROM
                        {schematab} s
                        LEFT JOIN LATERAL
                            UNNEST(s.mro) WITH ORDINALITY
                                      AS ancestry(ancestor, depth) ON true

                     UNION ALL
                     SELECT
                        s.id                AS id,
                        s.id                AS ancestor,
                        0                   AS depth
                     FROM
                        {schematab} s
                    ) AS cls

                    INNER JOIN {reftab} r
                        ON (((r.{refattr}).types[1]).maintype = cls.ancestor)
                ORDER BY
                    (cls.id, r.bases[1]), cls.depth
            '''.format(
                schematab=schematab,
                reftab='edgedb.{}'.format(refdict.ref_cls.__name__),
                refattr=q(refdict.backref_attr),
                src=dbname(sn.Name('std::source')),
                tgt=dbname(sn.Name('std::target')),
            )
        else:
            link_query = '''
                SELECT
                    {refattr}  AS {src},
                    id         AS {tgt}
                FROM
                    {reftab}
            '''.format(
                reftab='edgedb.{}'.format(refdict.ref_cls.__name__),
                refattr=q(refdict.backref_attr),
                src=dbname(sn.Name('std::source')),
                tgt=dbname(sn.Name('std::target')),
            )

        if pn.name == 'attributes':
            link_query = '''
                SELECT
                    q.*,
                    av.value    AS {valprop}
                FROM
                    ({query}
                    ) AS q
                    INNER JOIN edgedb.AttributeValue av
                        ON (q.{tgt} = ((av.attribute).types[1]).maintype AND
                            q.{src} = ((av.subject).types[1]).maintype)
            '''.format(
                query=link_query,
                src=dbname(sn.Name('std::source')),
                tgt=dbname(sn.Name('std::target')),
                valprop=dbname(sn.Name('schema::value')),
            )

            # In addition to custom attributes returned by the
            # generic refdict query above, collect and return
            # standard system attributes.
            partitions = []

            for metaclass in get_interesting_metaclasses():
                fields = metaclass.get_ownfields()
                attrs = []
                for fn in fields:
                    field = metaclass.get_field(fn)
                    if field.ephemeral:
                        continue

                    ftype = field.type[0]
                    if issubclass(ftype,
                                  (s_obj.Object, s_obj.ObjectCollection,
                                   typed.AbstractTypedCollection, list, dict)):
                        continue

                    aname = 'stdattrs::{}'.format(fn)
                    attrcls = schema.get(aname, default=None)
                    if attrcls is None:
                        raise RuntimeError(
                            'introspection schema error: {}.{} is not '
                            'defined as `stdattrs` Attribute'.format(
                                metaclass.__name__, fn))
                    aname = ql(aname) + '::text'
                    aval = q(fn) + '::text'
                    if fn == 'name':
                        aval = 'edgedb.get_shortname({})'.format(aval)
                    attrs.append([aname, aval])

                if attrs:
                    values = ', '.join('({}, {})'.format(k, v)
                                       for k, v in attrs)

                    qry = '''
                        SELECT
                            id AS subject_id,
                            a.*
                        FROM
                            {schematab},
                            UNNEST(ARRAY[{values}]) AS a(
                                attr_name text,
                                attr_value text
                            )
                    '''.format(schematab='edgedb.{}'.format(
                        metaclass.__name__),
                               values=values)

                    partitions.append(qry)

            if partitions:
                union = ('\n' + (' ' * 16) + 'UNION \n').join(partitions)

                stdattrs = '''
                    SELECT
                        vals.subject_id     AS {src},
                        attrs.id            AS {tgt},
                        vals.attr_value     AS {valprop}
                    FROM
                        ({union}
                        ) AS vals
                        INNER JOIN edgedb.Attribute attrs
                            ON (vals.attr_name = attrs.name)
                '''.format(
                    union=union,
                    src=dbname(sn.Name('std::source')),
                    tgt=dbname(sn.Name('std::target')),
                    valprop=dbname(sn.Name('schema::value')),
                )

                link_query += ('\n' + (' ' * 16) + 'UNION ALL (\n' + stdattrs +
                               '\n' + (' ' * 16) + ')')

    else:
        link_query = None
        if field is not None:
            ftype = field.type[0]
        else:
            ftype = type(None)

        if issubclass(ftype, (s_obj.ObjectSet, s_obj.ObjectList)):
            if ptr.singular():
                raise RuntimeError(
                    'introspection schema error: {!r} must not be '
                    'singular'.format('(' + schema_cls.name + ')' + '.' +
                                      pn.name))

            # ObjectSet and ObjectList fields are stored as uuid[],
            # so we just need to unnest the array here.
            refattr = 'UNNEST(' + q(pn.name) + ')'

        elif pn.name == 'params' and mcls is s_funcs.Function:
            # Func params need special handling as they are defined
            # in sevaral separate fields.
            link_query = f'''
                SELECT
                    q.id            AS {dbname('std::source')},
                    edgedb._derive_uuid(q.id, q.num::smallint)
                                    AS {dbname('std::target')}
                FROM
                    (SELECT
                        s.id        AS id,
                        t.num - 1   AS num
                     FROM
                        edgedb.{mcls.__name__} AS s,
                        LATERAL UNNEST((s.paramtypes).types)
                            WITH ORDINALITY AS
                                t(id, maintype, name, collection, subtypes,
                                  dimensions, is_root, num)
                     WHERE
                        t.is_root
                    ) AS q
            '''

        elif pn.name == 'params' and mcls is s_constraints.Constraint:
            # Constraint params need special handling as they are defined
            # in several separate fields.
            link_query = f'''
                SELECT
                    q.id            AS {dbname('std::source')},
                    edgedb._derive_uuid(q.id, q.num::smallint)
                                    AS {dbname('std::target')},
                    q.value         AS {dbname('schema::value')}
                FROM
                    (SELECT
                        s.id        AS id,
                        t.num - 1   AS num,
                        tv.value    AS value
                     FROM
                        edgedb.{mcls.__name__} AS s,
                        LATERAL UNNEST((s.paramtypes).types)
                            WITH ORDINALITY AS
                                t(id, maintype, name, collection, subtypes,
                                  dimensions, is_root, num)
                        LEFT JOIN
                            LATERAL UNNEST(s.args)
                                WITH ORDINALITY AS tv(value, num)
                            ON (t.num = tv.num)
                     WHERE
                        t.is_root
                    ) AS q
            '''

        elif issubclass(ftype, (s_obj.Object, s_obj.ObjectCollection)):
            # All other type fields are encoded as type_t.
            link_query = f'''
                SELECT
                    s.id        AS {dbname('std::source')},
                    (CASE WHEN t.collection IS NULL
                     THEN t.maintype ELSE t.id END)
                                AS {dbname('std::target')}
                FROM
                    edgedb.{mcls.__name__} AS s,
                    LATERAL UNNEST ((s.{q(pn.name)}).types) AS t(
                        id, maintype, name, collection,
                        subtypes, dimensions, is_root
                    )
                WHERE
                    t.is_root
            '''

        else:
            if not ptr.singular():
                raise RuntimeError('introspection schema error: {!r} must be '
                                   'singular'.format('(' + schema_cls.name +
                                                     ')' + '.' + pn.name))

            refattr = q(pn.name)

        if link_query is None:
            link_query = '''
                SELECT
                    id         AS {src},
                    {refattr}  AS {tgt}
                FROM
                    {schematab}
            '''.format(
                schematab='edgedb.{}'.format(mcls.__name__),
                refattr=refattr,
                src=dbname(sn.Name('std::source')),
                tgt=dbname(sn.Name('std::target')),
            )

    return dbops.View(name=tabname(ptr), query=link_query)
Example #10
0
async def generate_views(conn, schema):
    """Setup views the introspection schema.

    The introspection views emulate regular type and link tables
    for the classes in the "schema" module by querying the actual
    metadata tables.
    """
    commands = dbops.CommandGroup()

    # We use a separate schema to make it easy to redirect queries.
    commands.add_command(dbops.CreateSchema(name='edgedbss'))

    metaclasses = get_interesting_metaclasses()
    views = collections.OrderedDict()
    type_fields = []

    for mcls in metaclasses:
        if mcls is s_named.NamedObject:
            schema_name = 'Object'
        else:
            schema_name = mcls.__name__

        schema_cls = schema.get(sn.Name(module='schema', name=schema_name),
                                default=None)

        if schema_cls is None:
            # Not all schema metaclasses are represented in the
            # introspection schema, just ignore them.
            continue

        cols = []

        for pn, ptr in schema_cls.pointers.items():
            if ptr.is_pure_computable():
                continue

            field = mcls.get_field(pn.name)
            refdict = None
            if field is None:
                if pn.name == 'attributes':
                    # Special hack to allow generic introspection of
                    # both generic and standard attributes, so we
                    # pretend all classes are AttributeSubjects.
                    refdict = s_attrs.AttributeSubject.attributes

                elif issubclass(mcls, s_ref.ReferencingObject):
                    fn = classref_attr_aliases.get(pn.name, pn.name)
                    refdict = mcls.get_refdict(fn)
                    if refdict is not None and ptr.singular():
                        # This is nether a field, nor a refdict, that's
                        # not expected.
                        raise RuntimeError(
                            'introspection schema error: {!r} must not be '
                            'singular'.format('(' + schema_cls.name + ')' +
                                              '.' + pn.name))

            if field is None and refdict is None:
                if pn.name == 'id':
                    # Id is present implicitly in schema tables.
                    pass
                elif pn.name == '__type__':
                    continue
                elif pn.name == 'params' and (mcls is s_funcs.Function or mcls
                                              is s_constraints.Constraint):
                    # Function params need special handling as
                    # they are defined as three separate fields.
                    pass
                else:
                    # This is nether a field, nor a refdict, that's
                    # not expected.
                    raise RuntimeError(
                        'introspection schema error: cannot resolve '
                        '{!r} into metadata reference'.format('(' +
                                                              schema_cls.name +
                                                              ')' + '.' +
                                                              pn.name))

            if field is not None:
                ft = field.type[0]
                if (issubclass(ft, (s_obj.Object, s_obj.ObjectCollection)) and
                        not issubclass(ft,
                                       (s_obj.ObjectSet, s_obj.ObjectList))):
                    type_fields.append((f'edgedb.{mcls.__name__}', pn.name))

            ptrstor = types.get_pointer_storage_info(ptr, schema=schema)

            if ptrstor.table_type == 'ObjectType':
                if (pn.name == 'name' and issubclass(
                        mcls,
                    (s_inheriting.InheritingObject, s_funcs.Function))):
                    col_expr = 'edgedb.get_shortname(t.{})'.format(q(pn.name))
                else:
                    col_expr = f't.{q(pn.name)}'

                cols.append((col_expr, dbname(ptr.shortname)))
            else:
                view = _get_link_view(mcls, schema_cls, field, ptr, refdict,
                                      schema)
                if view.name not in views:
                    views[view.name] = view

        coltext = textwrap.indent(
            ',\n'.join(('{} AS {}'.format(*c) for c in cols)), ' ' * 16)

        view_query = f'''
            SELECT
                {coltext.strip()},
                no.id AS "std::__type__"
            FROM
                edgedb.{mcls.__name__} AS t
                INNER JOIN pg_class AS c
                    ON (t.tableoid = c.oid)
                INNER JOIN pg_description AS cmt
                    ON (c.oid = cmt.objoid AND c.tableoid = cmt.classoid)
                INNER JOIN edgedb.NamedObject AS no
                    ON (no.name = cmt.description)
        '''

        view = dbops.View(name=tabname(schema_cls), query=view_query)

        views[view.name] = view

    type_views = _generate_types_views(schema, type_fields)
    views.update({v.name: v for v in type_views})
    for v in type_views:
        views.move_to_end(v.name, last=False)

    te_view = _generate_type_element_view(schema, type_fields)
    views[te_view.name] = te_view

    fp_view = _generate_param_view(schema)
    views[fp_view.name] = fp_view

    types_view = views[tabname(schema.get('schema::Type'))]
    types_view.query += '\nUNION ALL\n' + '\nUNION ALL\n'.join(f'''
        (
            SELECT
                "schema::name",
                "schema::description",
                "std::id",
                "std::__type__"
            FROM
                {common.qname(*view.name)}
        )
    ''' for view in type_views)

    for view in views.values():
        commands.add_command(dbops.CreateView(view))

    await commands.execute(Context(conn))
Example #11
0
        dbops.CreateFunction(LinkNameToTableNameFunction()),
        dbops.CreateFunction(IssubclassFunction()),
        dbops.CreateFunction(IssubclassFunction2()),
        dbops.CreateFunction(IsinstanceFunction()),
        dbops.CreateFunction(NormalizeNameFunction()),
        dbops.CreateFunction(OrFilterFunction()),
    ])

    await commands.execute(Context(conn))


classref_attr_aliases = {'links': 'pointers', 'link_properties': 'pointers'}


dbname = lambda n: \
    common.quote_ident(common.edgedb_name_to_pg_name(sn.Name(n)))
tabname = lambda obj: \
    ('edgedbss', common.get_table_name(obj, catenate=False)[1])
q = common.quote_ident
ql = common.quote_literal


def _get_link_view(mcls, schema_cls, field, ptr, refdict, schema):
    pn = ptr.shortname

    if refdict:
        if (issubclass(mcls, s_inheriting.InheritingObject)
                or mcls is s_named.NamedObject):

            if mcls is s_named.NamedObject:
                schematab = 'edgedb.InheritingObject'
Example #12
0
 def _cb(name):
     clsid = class_map.get(name)
     if clsid:
         return sn.Name(module='__class__', name=str(clsid))
     else:
         return name
Example #13
0
 def __init__(self, element_name):
     super().__init__(
         name=s_name.Name(module='__tuple__', name=str(element_name)))
Example #14
0
import functools
import typing

from edgedb.lang.ir import utils as irutils

from edgedb.lang.schema import scalars as s_scalars
from edgedb.lang.schema import objtypes as s_objtypes
from edgedb.lang.schema import name as sn
from edgedb.lang.schema import objects as s_obj
from edgedb.lang.schema import schema as s_schema
from edgedb.lang.schema import types as s_types

from . import common

base_type_name_map = {
    sn.Name('std::str'): 'text',
    sn.Name('std::int64'): 'bigint',
    sn.Name('std::int32'): 'integer',
    sn.Name('std::int16'): 'smallint',
    sn.Name('std::decimal'): 'numeric',
    sn.Name('std::bool'): 'boolean',
    sn.Name('std::float64'): 'float8',
    sn.Name('std::float32'): 'float4',
    sn.Name('std::uuid'): 'uuid',
    sn.Name('std::datetime'): 'timestamptz',
    sn.Name('std::date'): 'date',
    sn.Name('std::time'): 'timetz',
    sn.Name('std::timedelta'): 'interval',
    sn.Name('std::bytes'): 'bytea',
    sn.Name('std::json'): 'jsonb',
}