Beispiel #1
0
def _compile_shape(ir_set: irast.Set, shape: typing.List[irast.Set], *,
                   ctx: context.CompilerContextLevel) -> pgast.TupleVar:

    result = shapecomp.compile_shape(ir_set, shape, ctx=ctx)

    for element in result.elements:
        # The ref might have already been added by the nested shape
        # processing, so add it conditionally.
        pathctx.put_path_var_if_not_exists(ctx.rel,
                                           element.path_id,
                                           element.val,
                                           aspect='serialized',
                                           env=ctx.env)

    ser_elements = []
    for el in result.elements:
        ser_val = pathctx.get_path_serialized_or_value_var(ctx.rel,
                                                           el.path_id,
                                                           env=ctx.env)
        ser_elements.append(
            pgast.TupleElement(path_id=el.path_id, name=el.name, val=ser_val))

    ser_result = pgast.TupleVar(elements=ser_elements, named=True)
    sval = output.serialize_expr(ser_result,
                                 path_id=ir_set.path_id,
                                 env=ctx.env)
    pathctx.put_path_serialized_var(ctx.rel,
                                    ir_set.path_id,
                                    sval,
                                    force=True,
                                    env=ctx.env)

    return result
def compile_shape(
        ir_set: irast.Set, shape: typing.List[irast.Set], *,
        ctx: context.CompilerContextLevel) -> pgast.TupleVar:
    elements = []

    with ctx.newscope() as shapectx:
        shapectx.disable_semi_join.add(ir_set.path_id)
        shapectx.unique_paths.add(ir_set.path_id)

        for el in shape:
            rptr = el.rptr
            ptrcls = rptr.ptrcls
            ptrdir = rptr.direction or s_pointers.PointerDirection.Outbound
            is_singleton = ptrcls.singular(ptrdir)

            if (irutils.is_subquery_set(el) or
                    isinstance(el.scls, s_objtypes.ObjectType) or
                    not is_singleton or
                    not ptrcls.required):
                wrapper = relgen.set_as_subquery(
                    el, as_value=True, ctx=shapectx)
                if not is_singleton:
                    value = relgen.set_to_array(
                        ir_set=el, query=wrapper, ctx=shapectx)
                else:
                    value = wrapper
            else:
                value = dispatch.compile(el, ctx=shapectx)

            elements.append(astutils.tuple_element_for_shape_el(el, value))

    return pgast.TupleVar(elements=elements, named=True)
Beispiel #3
0
def strip_output_var(
        var: pgast.OutputVar,
        *,
        optional: typing.Optional[bool] = None,
        nullable: typing.Optional[bool] = None) -> pgast.OutputVar:

    if isinstance(var, pgast.TupleVar):
        elements = []

        for el in var.elements:
            if isinstance(el.name, str):
                val = pgast.ColumnRef(name=[el.name])
            else:
                val = strip_output_var(el.name)

            elements.append(
                pgast.TupleElement(path_id=el.path_id, name=el.name, val=val))

        result = pgast.TupleVar(elements, named=var.named)
    else:
        result = pgast.ColumnRef(
            name=[var.name[-1]],
            optional=optional if optional is not None else var.optional,
            nullable=nullable if nullable is not None else var.nullable,
        )

    return result
Beispiel #4
0
def compile_shape(ir_set: irast.Set, shape: typing.List[irast.Set], *,
                  ctx: context.CompilerContextLevel) -> pgast.TupleVar:
    elements = []

    with ctx.newscope() as shapectx:
        shapectx.disable_semi_join.add(ir_set.path_id)

        if (isinstance(ir_set.expr, irast.Stmt)
                and ir_set.expr.iterator_stmt is not None):
            # The source set for this shape is a FOR statement,
            # which is special in that besides set path_id it
            # should also expose the path_id of the FOR iterator
            # so that shape element expressions that might contain
            # an iterator reference find it properly.
            #
            # So, for:
            #    SELECT Bar {
            #        foo := (FOR x := ... UNION Foo { spam := x })
            #    }
            #
            # the path scope when processing the shape of Bar.foo
            # should be {'Bar.foo', 'x'}.
            iter_path_id = ir_set.expr.iterator_stmt.path_id
            shapectx.path_scope[iter_path_id] = ctx.rel

        for el in shape:
            rptr = el.rptr
            ptrcls = rptr.ptrcls
            ptrdir = rptr.direction or s_pointers.PointerDirection.Outbound
            is_singleton = ptrcls.singular(ctx.env.schema, ptrdir)

            if (irutils.is_subquery_set(el)
                    or isinstance(el.stype, s_objtypes.ObjectType)
                    or not is_singleton
                    or not ptrcls.get_required(ctx.env.schema)):
                wrapper = relgen.set_as_subquery(el,
                                                 as_value=True,
                                                 ctx=shapectx)
                if not is_singleton:
                    value = relgen.set_to_array(ir_set=el,
                                                query=wrapper,
                                                ctx=shapectx)
                else:
                    value = wrapper
            else:
                value = dispatch.compile(el, ctx=shapectx)

            elements.append(
                astutils.tuple_element_for_shape_el(el, value, ctx=shapectx))

    return pgast.TupleVar(elements=elements, named=True)
Beispiel #5
0
def get_rvar_var(rvar: pgast.BaseRangeVar,
                 var: pgast.OutputVar) -> pgast.OutputVar:

    assert isinstance(var, pgast.OutputVar)

    if isinstance(var, pgast.TupleVar):
        elements = []

        for el in var.elements:
            val = get_rvar_var(rvar, el.name)
            elements.append(
                pgast.TupleElement(path_id=el.path_id, name=el.name, val=val))

        fieldref = pgast.TupleVar(elements, named=var.named)
    else:
        fieldref = get_column(rvar, var)

    return fieldref
Beispiel #6
0
def compile_Tuple(expr: irast.Base, *,
                  ctx: context.CompilerContextLevel) -> pgast.Base:
    ttype = _infer_type(expr, ctx=ctx)
    ttypes = ttype.element_types
    telems = list(ttypes)

    path_id = irast.PathId(ttype)

    elements = []

    for i, e in enumerate(expr.elements):
        telem = telems[i]
        ttype = ttypes[telem]
        el_path_id = irutils.tuple_indirection_path_id(path_id, telem, ttype)
        val = dispatch.compile(e.val, ctx=ctx)
        elements.append(pgast.TupleElement(path_id=el_path_id, val=val))

    result = pgast.TupleVar(elements=elements)

    return output.output_as_value(result, env=ctx.env)
Beispiel #7
0
def get_rvar_var(
        rvar: typing.Optional[pgast.BaseRangeVar], var: pgast.OutputVar,
        *, optional: bool=False, nullable: bool=None) \
        -> typing.Union[pgast.ColumnRef, pgast.TupleVar]:

    assert isinstance(var, pgast.OutputVar)

    if isinstance(var, pgast.TupleVar):
        elements = []

        for el in var.elements:
            val = get_rvar_var(rvar, el.name)
            elements.append(
                pgast.TupleElement(path_id=el.path_id, name=el.name, val=val))

        fieldref = pgast.TupleVar(elements, named=var.named)
    else:
        fieldref = get_column(rvar, var, optional=optional, nullable=nullable)

    return fieldref
Beispiel #8
0
def get_rvar_fieldref(
        rvar: typing.Optional[pgast.BaseRangeVar],
        colname: typing.Union[str, pgast.TupleVar],
        *, optional: bool=False, nullable: bool=None) \
        -> typing.Union[pgast.ColumnRef, pgast.TupleVar]:

    if isinstance(colname, pgast.TupleVar):
        elements = []

        for el in colname.elements:
            val = get_rvar_fieldref(rvar, el.name)
            elements.append(
                pgast.TupleElement(path_id=el.path_id, name=el.name, val=val))

        fieldref = pgast.TupleVar(elements, named=colname.named)
    else:
        fieldref = get_column(rvar,
                              colname,
                              optional=optional,
                              nullable=nullable)

    return fieldref
Beispiel #9
0
def process_insert_body(ir_stmt: irast.MutatingStmt, wrapper: pgast.Query,
                        insert_cte: pgast.CommonTableExpr,
                        insert_rvar: pgast.BaseRangeVar, *,
                        ctx: context.CompilerContextLevel) -> None:
    """Generate SQL DML CTEs from an InsertStmt IR.

    :param ir_stmt:
        IR of the statement.
    :param wrapper:
        Top-level SQL query.
    :param insert_cte:
        CTE representing the SQL INSERT to the main relation of the Object.
    """
    cols = [pgast.ColumnRef(name=['std::__type__'])]
    select = pgast.SelectStmt(target_list=[])
    values = select.target_list

    # The main INSERT query of this statement will always be
    # present to insert at least the std::id and std::__type__
    # links.
    insert_stmt = insert_cte.query

    insert_stmt.cols = cols
    insert_stmt.select_stmt = select

    if ir_stmt.parent_stmt is not None:
        iterator_set = ir_stmt.parent_stmt.iterator_stmt
    else:
        iterator_set = None

    if iterator_set is not None:
        with ctx.substmt() as ictx:
            ictx.path_scope = ictx.path_scope.new_child()
            ictx.path_scope[iterator_set.path_id] = ictx.rel
            clauses.compile_iterator_expr(ictx.rel, iterator_set, ctx=ictx)
            ictx.rel.path_id = iterator_set.path_id
            pathctx.put_path_bond(ictx.rel, iterator_set.path_id)
            iterator_cte = pgast.CommonTableExpr(
                query=ictx.rel, name=ctx.env.aliases.get('iter'))
            ictx.toplevel_stmt.ctes.append(iterator_cte)
        iterator_rvar = dbobj.rvar_for_rel(iterator_cte, env=ctx.env)
        relctx.include_rvar(select,
                            iterator_rvar,
                            path_id=ictx.rel.path_id,
                            ctx=ctx)
        iterator_id = pathctx.get_path_identity_var(select,
                                                    iterator_set.path_id,
                                                    env=ctx.env)
    else:
        iterator_cte = None
        iterator_id = None

    values.append(
        pgast.ResTarget(val=pgast.SelectStmt(
            target_list=[pgast.ResTarget(val=pgast.ColumnRef(name=['id']))],
            from_clause=[
                pgast.RangeVar(relation=pgast.Relation(name='objecttype',
                                                       schemaname='edgedb'))
            ],
            where_clause=astutils.new_binop(
                op=ast.ops.EQ,
                lexpr=pgast.ColumnRef(name=['name']),
                rexpr=pgast.Constant(val=ir_stmt.subject.scls.shortname)))))

    external_inserts = []
    tuple_elements = []
    parent_link_props = []

    with ctx.newrel() as subctx:
        subctx.rel = select
        subctx.rel_hierarchy[select] = insert_stmt

        subctx.expr_exposed = False
        subctx.shape_format = context.ShapeFormat.FLAT

        if iterator_cte is not None:
            subctx.path_scope = ctx.path_scope.new_child()
            subctx.path_scope[iterator_cte.query.path_id] = select

        # Process the Insert IR and separate links that go
        # into the main table from links that are inserted into
        # a separate link table.
        for shape_el in ir_stmt.subject.shape:
            rptr = shape_el.rptr
            ptrcls = rptr.ptrcls.material_type()

            if (ptrcls.is_link_property()
                    and rptr.source.path_id != ir_stmt.subject.path_id):
                parent_link_props.append(shape_el)
                continue

            ptr_info = pg_types.get_pointer_storage_info(
                ptrcls,
                schema=subctx.env.schema,
                resolve_type=True,
                link_bias=False)

            props_only = False

            # First, process all local link inserts.
            if ptr_info.table_type == 'ObjectType':
                props_only = True
                field = pgast.ColumnRef(name=[ptr_info.column_name])
                cols.append(field)

                insvalue = insert_value_for_shape_element(insert_stmt,
                                                          wrapper,
                                                          ir_stmt,
                                                          shape_el,
                                                          iterator_id,
                                                          ptr_info=ptr_info,
                                                          ctx=subctx)

                tuple_el = astutils.tuple_element_for_shape_el(shape_el, field)
                tuple_elements.append(tuple_el)
                values.append(pgast.ResTarget(val=insvalue))

            ptr_info = pg_types.get_pointer_storage_info(ptrcls,
                                                         resolve_type=False,
                                                         link_bias=True)

            if ptr_info and ptr_info.table_type == 'link':
                external_inserts.append((shape_el, props_only))

        if iterator_cte is not None:
            cols.append(pgast.ColumnRef(name=['__edb_token']))

            values.append(pgast.ResTarget(val=iterator_id))

            pathctx.put_path_identity_var(insert_stmt,
                                          iterator_set.path_id,
                                          cols[-1],
                                          force=True,
                                          env=subctx.env)

            pathctx.put_path_bond(insert_stmt, iterator_set.path_id)

    toplevel = ctx.toplevel_stmt
    toplevel.ctes.append(insert_cte)

    # Process necessary updates to the link tables.
    for shape_el, props_only in external_inserts:
        process_link_update(ir_stmt,
                            shape_el,
                            props_only,
                            wrapper,
                            insert_cte,
                            iterator_cte,
                            ctx=ctx)

    if parent_link_props:
        prop_elements = []

        with ctx.newscope() as scopectx:
            scopectx.rel = wrapper

            for shape_el in parent_link_props:
                rptr = shape_el.rptr
                scopectx.path_scope[rptr.source.path_id] = wrapper
                pathctx.put_path_rvar_if_not_exists(wrapper,
                                                    rptr.source.path_id,
                                                    insert_rvar,
                                                    aspect='value',
                                                    env=scopectx.env)
                dispatch.compile(shape_el, ctx=scopectx)
                tuple_el = astutils.tuple_element_for_shape_el(shape_el, None)
                prop_elements.append(tuple_el)

        valtuple = pgast.TupleVar(elements=prop_elements, named=True)
        pathctx.put_path_value_var(wrapper,
                                   ir_stmt.subject.path_id,
                                   valtuple,
                                   force=True,
                                   env=ctx.env)
Beispiel #10
0
def _get_path_output(rel: pgast.BaseRelation,
                     path_id: irast.PathId,
                     *,
                     aspect: str,
                     allow_nullable: bool = True,
                     ptr_info: typing.Optional[
                         pg_types.PointerStorageInfo] = None,
                     env: context.Environment) -> pgast.OutputVar:

    result = rel.path_outputs.get((path_id, aspect))
    if result is not None:
        return result

    if is_terminal_relation(rel):
        return _get_rel_path_output(rel,
                                    path_id,
                                    aspect=aspect,
                                    ptr_info=ptr_info,
                                    env=env)
    else:
        ref = get_path_var(rel, path_id, aspect=aspect, env=env)

    other_output = find_path_output(rel, path_id, ref, env=env)
    if other_output is not None:
        rel.path_outputs[path_id, aspect] = other_output
        return other_output

    if isinstance(ref, pgast.TupleVar):
        elements = []
        for el in ref.elements:
            el_path_id = reverse_map_path_id(el.path_id, rel.view_path_id_map)

            try:
                # Similarly to get_path_var(), check for outer path_id
                # first for tuple serialized var disambiguation.
                element = _get_path_output(rel,
                                           el_path_id,
                                           aspect=aspect,
                                           allow_nullable=False,
                                           env=env)
            except LookupError:
                element = get_path_output(rel,
                                          el_path_id,
                                          aspect=aspect,
                                          allow_nullable=False,
                                          env=env)

            elements.append(
                pgast.TupleElement(path_id=el_path_id, name=element))

        result = pgast.TupleVar(elements=elements, named=ref.named)

    else:
        if astutils.is_set_op_query(rel):
            assert isinstance(ref, pgast.ColumnRef)
            result = dbobj.get_column(None, ref)
        else:
            alias = get_path_output_alias(path_id, aspect, env=env)

            restarget = pgast.ResTarget(name=alias, val=ref)
            if hasattr(rel, 'returning_list'):
                rel.returning_list.append(restarget)
            else:
                rel.target_list.append(restarget)

            nullable = is_nullable(ref, env=env)

            if isinstance(ref, pgast.ColumnRef):
                optional = ref.optional
            else:
                optional = None

            if nullable and not allow_nullable:
                var = get_path_var(rel, path_id, aspect=aspect, env=env)
                rel.where_clause = astutils.extend_binop(
                    rel.where_clause, pgast.NullTest(arg=var, negated=True))
                nullable = False

            result = pgast.ColumnRef(name=[alias],
                                     nullable=nullable,
                                     optional=optional)

    rel.path_outputs[path_id, aspect] = result
    return result
Beispiel #11
0
def _compile_shape(ir_set: irast.Set, shape: typing.List[irast.Set], *,
                   ctx: context.CompilerContextLevel) -> pgast.TupleVar:
    elements = []

    with ctx.newscope() as shapectx:
        shapectx.disable_semi_join.add(ir_set.path_id)
        shapectx.unique_paths.add(ir_set.path_id)

        for el in shape:
            rptr = el.rptr
            ptrcls = rptr.ptrcls
            ptrdir = rptr.direction or s_pointers.PointerDirection.Outbound
            is_singleton = ptrcls.singular(ptrdir)

            if (irutils.is_subquery_set(el)
                    or isinstance(el.scls, s_objtypes.ObjectType)
                    or not is_singleton or not ptrcls.required):
                wrapper = relgen.set_as_subquery(el,
                                                 as_value=True,
                                                 ctx=shapectx)
                if not is_singleton:
                    value = relgen.set_to_array(ir_set=el,
                                                query=wrapper,
                                                ctx=shapectx)
                else:
                    value = wrapper
            else:
                value = dispatch.compile(el, ctx=shapectx)

            elements.append(astutils.tuple_element_for_shape_el(el, value))

    result = pgast.TupleVar(elements=elements, named=True)
    pathctx.put_path_value_var(ctx.rel,
                               ir_set.path_id,
                               result,
                               force=True,
                               env=ctx.env)

    for element in elements:
        # The ref might have already been added by the nested shape
        # processing, so add it conditionally.
        pathctx.put_path_value_var_if_not_exists(ctx.rel,
                                                 element.path_id,
                                                 element.val,
                                                 env=ctx.env)

    if output.in_serialization_ctx(ctx):
        ser_elements = []
        for el in elements:
            ser_val = pathctx.get_path_serialized_or_value_var(ctx.rel,
                                                               el.path_id,
                                                               env=ctx.env)
            ser_elements.append(
                pgast.TupleElement(path_id=el.path_id,
                                   name=el.name,
                                   val=ser_val))

        ser_result = pgast.TupleVar(elements=ser_elements, named=True)
        sval = output.serialize_expr(ser_result, env=ctx.env)
        pathctx.put_path_serialized_var(ctx.rel,
                                        ir_set.path_id,
                                        sval,
                                        force=True,
                                        env=ctx.env)

    return result
Beispiel #12
0
def get_path_output(rel: pgast.BaseRelation,
                    path_id: irast.PathId,
                    *,
                    aspect: str,
                    ptr_info: typing.Optional[
                        pg_types.PointerStorageInfo] = None,
                    env: context.Environment) -> pgast.OutputVar:

    view_path_id_map = getattr(rel, 'view_path_id_map', None)
    if view_path_id_map:
        path_id = map_path_id(path_id, view_path_id_map)

    result = rel.path_outputs.get((path_id, aspect))
    if result is not None:
        return result

    if isinstance(rel, pgast.Relation):
        return _get_rel_path_output(rel,
                                    path_id,
                                    aspect=aspect,
                                    ptr_info=ptr_info,
                                    env=env)
    else:
        ref = get_path_var(rel, path_id, aspect=aspect, env=env)

    other_output = find_path_output(rel, path_id, ref, env=env)
    if other_output is not None:
        rel.path_outputs[path_id, aspect] = other_output
        return other_output

    if isinstance(ref, pgast.TupleVar):
        elements = []
        for el in ref.elements:
            el_path_id = reverse_map_path_id(el.path_id, rel.view_path_id_map)
            element = get_path_output(rel, el_path_id, aspect=aspect, env=env)
            elements.append(
                pgast.TupleElement(path_id=el_path_id, name=element))
        result = pgast.TupleVar(elements=elements, named=ref.named)

    else:
        if astutils.is_set_op_query(rel):
            assert isinstance(ref, pgast.ColumnRef)
            result = dbobj.get_column(None, ref)
        else:
            alias = get_path_output_alias(path_id, aspect, env=env)

            restarget = pgast.ResTarget(name=alias, val=ref)
            if hasattr(rel, 'returning_list'):
                rel.returning_list.append(restarget)
            else:
                rel.target_list.append(restarget)

            if isinstance(ref, pgast.ColumnRef):
                nullable = ref.nullable
                optional = ref.optional
            else:
                nullable = rel.nullable
                optional = None

            result = pgast.ColumnRef(name=[alias],
                                     nullable=nullable,
                                     optional=optional)

    rel.path_outputs[path_id, aspect] = result
    return result