示例#1
0
文件: expr.py 项目: zhutony/edgedb
def compile_Tuple(expr: irast.Tuple, *,
                  ctx: context.CompilerContextLevel) -> pgast.BaseExpr:
    ttype = expr.typeref
    ttypes = {}
    for i, st in enumerate(ttype.subtypes):
        if st.element_name:
            ttypes[st.element_name] = st
        else:
            ttypes[str(i)] = st
    telems = list(ttypes)

    elements = []

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

    result = pgast.TupleVar(elements=elements, typeref=ttype)

    return output.output_as_value(result, env=ctx.env)
示例#2
0
文件: expr.py 项目: sthagen/edgedb
def _compile_shape(ir_set: irast.Set, shape: List[Tuple[irast.Set,
                                                        qlast.ShapeOp]], *,
                   ctx: context.CompilerContextLevel) -> pgast.TupleVar:

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

    for element in result.elements:
        # We want to force, because the path id might already exist
        # serialized with a different shape, and we need ours to be
        # visible. (Anything needing the old one needs to have pulled
        # it already: see the "unfortunate hack" in
        # process_set_as_tuple.)
        pathctx.put_path_serialized_var(ctx.rel,
                                        element.path_id,
                                        element.val,
                                        force=True,
                                        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
示例#3
0
文件: dml.py 项目: willingc/edgedb
def process_insert_body(ir_stmt: irast.MutatingStmt, wrapper: pgast.Query,
                        insert_cte: pgast.CommonTableExpr,
                        insert_rvar: pgast.PathRangeVar, *,
                        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=['__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 `id` and `__type__`
    # properties.
    insert_stmt = insert_cte.query
    assert isinstance(insert_stmt, pgast.InsertStmt)

    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 = relctx.rvar_for_rel(iterator_cte, ctx=ctx)
        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

    typeref = ir_stmt.subject.typeref
    if typeref.material_type is not None:
        typeref = typeref.material_type

    values.append(
        pgast.ResTarget(val=pgast.TypeCast(
            arg=pgast.StringConstant(val=str(typeref.id)),
            type_name=pgast.TypeName(name=('uuid', ))), ))

    external_inserts = []
    parent_link_props = []

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

        subctx.expr_exposed = False

        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
            ptrref = rptr.ptrref
            if ptrref.material_ptr is not None:
                ptrref = ptrref.material_ptr

            if (ptrref.parent_ptr is not None
                    and rptr.source.path_id != ir_stmt.subject.path_id):
                parent_link_props.append(shape_el)
                continue

            ptr_info = pg_types.get_ptrref_storage_info(ptrref,
                                                        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)

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

            ptr_info = pg_types.get_ptrref_storage_info(ptrref,
                                                        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=ir_stmt,
                            ir_set=shape_el,
                            props_only=props_only,
                            wrapper=wrapper,
                            dml_cte=insert_cte,
                            iterator_cte=iterator_cte,
                            is_insert=True,
                            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.visit(shape_el, ctx=scopectx)
                tuple_el = astutils.tuple_element_for_shape_el(shape_el,
                                                               None,
                                                               ctx=scopectx)
                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)
示例#4
0
def compile_shape(ir_set: irast.Set, shape: Sequence[Tuple[irast.Set,
                                                           qlast.ShapeOp]], *,
                  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):
            # 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'}.
            iterator = ir_set.expr.iterator_stmt
            if iterator:
                shapectx.path_scope[iterator.path_id] = ctx.rel

        for el, op in shape:
            if op == qlast.ShapeOp.MATERIALIZE and not ctx.materializing:
                continue

            rptr = el.rptr
            assert rptr is not None
            ptrref = rptr.ptrref
            # As an implementation expedient, we currently represent
            # AT_MOST_ONE materialized values with arrays
            card = ptrref.dir_cardinality
            is_singleton = (card.is_single()
                            and (op != qlast.ShapeOp.MATERIALIZE
                                 or not card.can_be_zero()))
            value: pgast.BaseExpr

            if (irutils.is_subquery_set(el) or el.path_id.is_objtype_path()
                    or not is_singleton or not ptrref.required):
                wrapper = relgen.set_as_subquery(el,
                                                 as_value=True,
                                                 ctx=shapectx)
                if not is_singleton:
                    value = relctx.set_to_array(path_id=el.path_id,
                                                query=wrapper,
                                                ctx=shapectx)
                else:
                    value = wrapper
            else:
                value = dispatch.compile(el, ctx=shapectx)

            tuple_el = astutils.tuple_element_for_shape_el(el,
                                                           value,
                                                           ctx=shapectx)

            assert isinstance(tuple_el, pgast.TupleElement)
            elements.append(tuple_el)

    return pgast.TupleVar(elements=elements, named=True)
示例#5
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

    alias = None
    rptr = path_id.rptr()
    if rptr is not None and irtyputils.is_id_ptrref(rptr):
        # A value reference to Object.id is the same as a value
        # reference to the Object itself.
        src_path_id = path_id.src_path()
        id_output = rel.path_outputs.get((src_path_id, 'value'))
        if id_output is not None:
            _put_path_output_var(rel, path_id, aspect, id_output, env=env)
            return id_output

    if is_terminal_relation(rel):
        return _get_rel_path_output(rel,
                                    path_id,
                                    aspect=aspect,
                                    ptr_info=ptr_info,
                                    env=env)
    elif is_values_relation(rel):
        # The VALUES() construct seems to always expose its
        # value as "column1".
        alias = 'column1'
        ref = pgast.ColumnRef(name=[alias])
    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:
        _put_path_output_var(rel, path_id, aspect, other_output, env=env)
        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):
            result = dbobj.strip_output_var(ref)
        else:
            if alias is None:
                alias = get_path_output_alias(path_id, aspect, env=env)

            restarget = pgast.ResTarget(name=alias,
                                        val=ref,
                                        ser_safe=getattr(
                                            ref, 'ser_safe', False))
            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)

    _put_path_output_var(rel, path_id, aspect, result, env=env)
    if (aspect == 'identity' and path_id.is_objtype_path()
            and (path_id, 'value') not in rel.path_outputs):
        _put_path_output_var(rel, path_id, 'value', result, env=env)

    return result