コード例 #1
0
ファイル: astutils.py プロジェクト: xing0713/edgedb
def join_condition(lref: pgast.ColumnRef, rref: pgast.ColumnRef) -> pgast.Base:
    path_cond = new_binop(lref, rref, op='=')

    if lref.optional:
        opt_cond = pgast.NullTest(arg=lref)
        path_cond = extend_binop(path_cond, opt_cond, op='OR')

    if rref.optional:
        opt_cond = pgast.NullTest(arg=rref)
        path_cond = extend_binop(path_cond, opt_cond, op='OR')

    return path_cond
コード例 #2
0
ファイル: stmt.py プロジェクト: syyunn/edgedb
def compile_SelectStmt(
        stmt: irast.SelectStmt, *,
        ctx: context.CompilerContextLevel) -> pgast.BaseExpr:

    if ctx.singleton_mode:
        return dispatch.compile(stmt.result, ctx=ctx)

    parent_ctx = ctx
    with parent_ctx.substmt() as ctx:
        # Common setup.
        clauses.init_stmt(stmt, ctx=ctx, parent_ctx=parent_ctx)

        query = ctx.stmt

        if not isinstance(stmt.result.expr, irast.MutatingStmt):
            iterators = irutils.get_iterator_sets(stmt)
            for iterator_set in iterators:
                # Process FOR clause.
                clauses.compile_iterator_expr(query, iterator_set, ctx=ctx)

        # Process the result expression;
        outvar = clauses.compile_output(stmt.result, ctx=ctx)

        # The FILTER clause.
        if stmt.where is not None:
            query.where_clause = astutils.extend_binop(
                query.where_clause,
                clauses.compile_filter_clause(
                    stmt.where, stmt.where_card, ctx=ctx))

        if outvar.nullable and query is ctx.toplevel_stmt:
            # A nullable var has bubbled up to the top,
            # filter out NULLs.
            valvar: pgast.BaseExpr = pathctx.get_path_value_var(
                query, stmt.result.path_id, env=ctx.env)
            if isinstance(valvar, pgast.TupleVar):
                valvar = pgast.ImplicitRowExpr(
                    args=[e.val for e in valvar.elements])

            query.where_clause = astutils.extend_binop(
                query.where_clause,
                pgast.NullTest(arg=valvar, negated=True)
            )

        # The ORDER BY clause
        query.sort_clause = clauses.compile_orderby_clause(
            stmt.orderby, ctx=ctx)

        # The OFFSET clause
        query.limit_offset = clauses.compile_limit_offset_clause(
            stmt.offset, ctx=ctx)

        # The LIMIT clause
        query.limit_count = clauses.compile_limit_offset_clause(
            stmt.limit, ctx=ctx)

        clauses.fini_stmt(query, ctx, parent_ctx)

    return query
コード例 #3
0
def apply_volatility_ref(stmt: pgast.SelectStmt, *,
                         ctx: context.CompilerContextLevel) -> None:
    for ref in ctx.volatility_ref:
        # Apply the volatility reference.
        # See the comment in process_set_as_subquery().
        stmt.where_clause = astutils.extend_binop(
            stmt.where_clause, pgast.NullTest(
                arg=ref(),
                negated=True,
            ))
コード例 #4
0
ファイル: output.py プロジェクト: stjordanis/edgedb
def wrap_script_stmt(
    stmt: pgast.SelectStmt,
    *,
    suppress_all_output: bool = False,
    env: context.Environment,
) -> pgast.SelectStmt:

    subrvar = pgast.RangeSubselect(
        subquery=stmt, alias=pgast.Alias(aliasname=env.aliases.get('aggw')))

    stmt_res = stmt.target_list[0]

    if stmt_res.name is None:
        stmt_res = stmt.target_list[0] = pgast.ResTarget(
            name=env.aliases.get('v'),
            val=stmt_res.val,
        )
        assert stmt_res.name is not None

    count_val = pgast.FuncCall(name=('count', ),
                               args=[pgast.ColumnRef(name=[stmt_res.name])])

    result = pgast.SelectStmt(target_list=[
        pgast.ResTarget(
            val=count_val,
            name=stmt_res.name,
        ),
    ],
                              from_clause=[
                                  subrvar,
                              ])

    if suppress_all_output:
        subrvar = pgast.RangeSubselect(
            subquery=result, alias=pgast.Alias(aliasname=env.aliases.get('q')))

        result = pgast.SelectStmt(
            target_list=[],
            from_clause=[
                subrvar,
            ],
            where_clause=pgast.NullTest(arg=pgast.ColumnRef(
                name=[subrvar.alias.aliasname, stmt_res.name], ), ),
        )

    result.ctes = stmt.ctes
    result.argnames = stmt.argnames
    stmt.ctes = []

    return result
コード例 #5
0
def compile_iterator_expr(
        query: pgast.SelectStmt, iterator_expr: irast.Set, *,
        ctx: context.CompilerContextLevel) \
        -> pgast.PathRangeVar:

    assert isinstance(iterator_expr.expr, (irast.GroupStmt, irast.SelectStmt))

    with ctx.new() as subctx:
        subctx.expr_exposed = False
        subctx.rel = query

        already_existed = bool(
            relctx.maybe_get_path_rvar(query,
                                       iterator_expr.path_id,
                                       aspect='value',
                                       ctx=ctx))
        dispatch.visit(iterator_expr, ctx=subctx)
        iterator_rvar = relctx.get_path_rvar(query,
                                             iterator_expr.path_id,
                                             aspect='value',
                                             ctx=ctx)
        iterator_query = iterator_rvar.query

        # If the iterator value is nullable, add a null test. This
        # makes sure that we don't spuriously produce output when
        # iterating over options pointers.
        assert isinstance(iterator_query, pgast.SelectStmt)
        iterator_var = pathctx.get_path_value_var(
            iterator_query, path_id=iterator_expr.path_id, env=ctx.env)
        if iterator_var.nullable:
            iterator_query.where_clause = astutils.extend_binop(
                iterator_query.where_clause,
                pgast.NullTest(arg=iterator_var, negated=True))

        # Regardless of result type, we use transient identity,
        # for path identity of the iterator expression.  This is
        # necessary to maintain correct correlation for the state
        # of iteration in DML statements.
        # The already_existed check is to avoid adding in bogus volatility refs
        # when we reprocess an iterator that was hoisted.
        if not already_existed:
            relctx.ensure_bond_for_expr(iterator_expr.expr.result,
                                        iterator_query,
                                        ctx=subctx)

    return iterator_rvar
コード例 #6
0
def ensure_transient_identity_for_set(ir_set: irast.Set,
                                      stmt: pgast.BaseRelation,
                                      *,
                                      ctx: context.CompilerContextLevel,
                                      type: str = 'int') -> None:

    if type == 'uuid':
        id_expr = pgast.FuncCall(
            name=(
                'edgedbext',
                'uuid_generate_v1mc',
            ),
            args=[],
        )
    else:
        id_expr = pgast.FuncCall(name=('row_number', ),
                                 args=[],
                                 over=pgast.WindowDef())

    pathctx.put_path_identity_var(stmt,
                                  ir_set.path_id,
                                  id_expr,
                                  force=True,
                                  env=ctx.env)
    pathctx.put_path_bond(stmt, ir_set.path_id)

    if (ctx.volatility_ref is not None
            and ctx.volatility_ref is not context.NO_VOLATILITY
            and isinstance(stmt, pgast.SelectStmt)):
        # Apply the volatility reference.
        # See the comment in process_set_as_subquery().
        stmt.where_clause = astutils.extend_binop(
            stmt.where_clause,
            pgast.NullTest(
                arg=ctx.volatility_ref,
                negated=True,
            ))
コード例 #7
0
def compile_operator(expr: irast.OperatorCall, args: Sequence[pgast.BaseExpr],
                     *, ctx: context.CompilerContextLevel) -> pgast.BaseExpr:
    lexpr = rexpr = None
    result: Optional[pgast.BaseExpr] = None

    if expr.operator_kind is ql_ft.OperatorKind.Infix:
        lexpr, rexpr = args
    elif expr.operator_kind is ql_ft.OperatorKind.Prefix:
        rexpr = args[0]
    elif expr.operator_kind is ql_ft.OperatorKind.Postfix:
        lexpr = args[0]
    else:
        raise RuntimeError(f'unexpected operator kind: {expr.operator_kind!r}')

    str_func_name = str(expr.func_shortname)
    if ((str_func_name in {'std::=', 'std::!='}
         or str(expr.origin_name) in {'std::=', 'std::!='})
            and expr.args[0].expr.typeref is not None
            and irtyputils.is_object(expr.args[0].expr.typeref)
            and expr.args[1].expr.typeref is not None
            and irtyputils.is_object(expr.args[1].expr.typeref)):
        if str_func_name == 'std::=' or str(expr.origin_name) == 'std::=':
            sql_oper = '='
        else:
            sql_oper = '!='

    elif str_func_name == 'std::EXISTS':
        assert rexpr
        result = pgast.NullTest(arg=rexpr, negated=True)

    elif expr.sql_operator:
        sql_oper = expr.sql_operator[0]
        if len(expr.sql_operator) > 1:
            # Explicit operand types given in FROM SQL OPERATOR
            lexpr, rexpr = _cast_operands(lexpr, rexpr, expr.sql_operator[1:])

    elif expr.sql_function:
        sql_func = expr.sql_function[0]
        func_name = tuple(sql_func.split('.', 1))
        if len(expr.sql_function) > 1:
            # Explicit operand types given in FROM SQL FUNCTION
            lexpr, rexpr = _cast_operands(lexpr, rexpr, expr.sql_function[1:])

        args = []
        if lexpr is not None:
            args.append(lexpr)
        if rexpr is not None:
            args.append(rexpr)

        result = pgast.FuncCall(name=func_name, args=args)

    elif expr.origin_name is not None:
        sql_oper = common.get_operator_backend_name(expr.origin_name)[1]

    else:
        sql_oper = common.get_operator_backend_name(expr.func_shortname)[1]

    # If result was not already computed, it's going to be a generic Expr.
    if result is None:
        result = pgast.Expr(
            kind=pgast.ExprKind.OP,
            name=sql_oper,
            lexpr=lexpr,
            rexpr=rexpr,
        )

    if expr.force_return_cast:
        # The underlying operator has a return value type
        # different from that of the EdgeQL operator declaration,
        # so we need to make an explicit cast here.
        result = pgast.TypeCast(
            arg=result,
            type_name=pgast.TypeName(
                name=pg_types.pg_type_from_ir_typeref(expr.typeref)))

    return result
コード例 #8
0
ファイル: stmt.py プロジェクト: dmgolembiowski/edgedb
def compile_SelectStmt(stmt: irast.SelectStmt, *,
                       ctx: context.CompilerContextLevel) -> pgast.BaseExpr:

    if ctx.singleton_mode:
        return dispatch.compile(stmt.result, ctx=ctx)

    parent_ctx = ctx
    with parent_ctx.substmt() as ctx:
        # Common setup.
        clauses.init_stmt(stmt, ctx=ctx, parent_ctx=parent_ctx)

        for binding in (stmt.bindings or ()):
            # If something we are WITH binding contains DML, we want to
            # compile it *now*, in the context of its initial appearance
            # and not where the variable is used. This will populate
            # dml_stmts with the CTEs, which will be picked up when the
            # variable is referenced.
            if irutils.contains_dml(binding):
                with ctx.substmt() as bctx:
                    dispatch.compile(binding, ctx=bctx)

        query = ctx.stmt

        iterator_set = stmt.iterator_stmt
        last_iterator: Optional[irast.Set] = None
        if iterator_set and irutils.contains_dml(stmt):
            # If we have iterators and we contain nested DML
            # statements, we need to hoist the iterators into CTEs and
            # then explicitly join them back into the query.
            iterator = dml.compile_iterator_cte(iterator_set, ctx=ctx)
            ctx.path_scope = ctx.path_scope.new_child()
            dml.merge_iterator(iterator, ctx.rel, ctx=ctx)

            ctx.enclosing_cte_iterator = iterator
            last_iterator = stmt.iterator_stmt

        else:
            if iterator_set:
                # Process FOR clause.
                with ctx.new() as ictx:
                    clauses.setup_iterator_volatility(last_iterator, ctx=ictx)
                    iterator_rvar = clauses.compile_iterator_expr(query,
                                                                  iterator_set,
                                                                  ctx=ictx)
                for aspect in {'identity', 'value'}:
                    pathctx.put_path_rvar(
                        query,
                        path_id=iterator_set.path_id,
                        rvar=iterator_rvar,
                        aspect=aspect,
                        env=ctx.env,
                    )
                last_iterator = iterator_set

        # Process materialized sets
        clauses.compile_materialized_exprs(query, stmt, ctx=ctx)

        # Process the result expression.
        with ctx.new() as ictx:
            clauses.setup_iterator_volatility(last_iterator, ctx=ictx)
            outvar = clauses.compile_output(stmt.result, ctx=ictx)

        with ctx.new() as ictx:
            # FILTER and ORDER BY need to have the base result as a
            # volatility ref.
            clauses.setup_iterator_volatility(stmt.result, ctx=ictx)

            # The FILTER clause.
            if stmt.where is not None:
                query.where_clause = astutils.extend_binop(
                    query.where_clause,
                    clauses.compile_filter_clause(stmt.where,
                                                  stmt.where_card,
                                                  ctx=ctx))

            # The ORDER BY clause
            if stmt.orderby is not None:
                with ctx.new() as ictx:
                    query.sort_clause = clauses.compile_orderby_clause(
                        stmt.orderby, ctx=ictx)

        if outvar.nullable and query is ctx.toplevel_stmt:
            # A nullable var has bubbled up to the top,
            # filter out NULLs.
            valvar: pgast.BaseExpr = pathctx.get_path_value_var(
                query, stmt.result.path_id, env=ctx.env)
            if isinstance(valvar, pgast.TupleVar):
                valvar = pgast.ImplicitRowExpr(
                    args=[e.val for e in valvar.elements])

            query.where_clause = astutils.extend_binop(
                query.where_clause, pgast.NullTest(arg=valvar, negated=True))

        # The OFFSET clause
        query.limit_offset = clauses.compile_limit_offset_clause(stmt.offset,
                                                                 ctx=ctx)

        # The LIMIT clause
        query.limit_count = clauses.compile_limit_offset_clause(stmt.limit,
                                                                ctx=ctx)

        clauses.fini_stmt(query, ctx, parent_ctx)

    return query
コード例 #9
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

    ref: pgast.BaseExpr
    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)

    assert isinstance(rel, pgast.Query)
    if 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.TupleVarBase):
        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.TupleElementBase(path_id=el_path_id, name=element))

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

    else:
        if astutils.is_set_op_query(rel):
            assert isinstance(ref, pgast.OutputVar)
            result = astutils.strip_output_var(ref)
        else:
            assert isinstance(rel, pgast.ReturningQuery), \
                "expected ReturningQuery"

            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))
            rel.target_list.append(restarget)

            nullable = is_nullable(ref, env=env)

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

            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 (path_id.is_objtype_path()
            and not isinstance(result, pgast.TupleVarBase)):
        equiv_aspect = None
        if aspect == 'identity':
            equiv_aspect = 'value'
        elif aspect == 'value':
            equiv_aspect = 'identity'

        if (equiv_aspect is not None
                and (path_id, equiv_aspect) not in rel.path_outputs):
            _put_path_output_var(rel, path_id, equiv_aspect, result, env=env)

    return result
コード例 #10
0
ファイル: stmt.py プロジェクト: isidentical/edgedb
def compile_SelectStmt(stmt: irast.SelectStmt, *,
                       ctx: context.CompilerContextLevel) -> pgast.BaseExpr:

    if ctx.singleton_mode:
        return dispatch.compile(stmt.result, ctx=ctx)

    parent_ctx = ctx
    with parent_ctx.substmt() as ctx:
        # Common setup.
        clauses.init_stmt(stmt, ctx=ctx, parent_ctx=parent_ctx)

        query = ctx.stmt

        iterators = irutils.get_iterator_sets(stmt)
        if iterators and irutils.contains_dml(stmt):
            # If we have iterators and we contain nested DML
            # statements, we need to hoist the iterators into CTEs and
            # then explicitly join them back into the query.
            iterator = dml.compile_iterator_ctes(iterators, ctx=ctx)
            ctx.path_scope = ctx.path_scope.new_child()
            dml.merge_iterator(iterator, ctx.rel, ctx=ctx)

            ctx.enclosing_cte_iterator = iterator

        else:
            iterator = None
            for iterator_set in iterators:
                # Process FOR clause.
                iterator_rvar = clauses.compile_iterator_expr(query,
                                                              iterator_set,
                                                              ctx=ctx)
                for aspect in {'identity', 'value'}:
                    pathctx.put_path_rvar(
                        query,
                        path_id=iterator_set.path_id,
                        rvar=iterator_rvar,
                        aspect=aspect,
                        env=ctx.env,
                    )

        # Process the result expression;
        outvar = clauses.compile_output(stmt.result, ctx=ctx)

        # The FILTER clause.
        if stmt.where is not None:
            query.where_clause = astutils.extend_binop(
                query.where_clause,
                clauses.compile_filter_clause(stmt.where,
                                              stmt.where_card,
                                              ctx=ctx))

        if outvar.nullable and query is ctx.toplevel_stmt:
            # A nullable var has bubbled up to the top,
            # filter out NULLs.
            valvar: pgast.BaseExpr = pathctx.get_path_value_var(
                query, stmt.result.path_id, env=ctx.env)
            if isinstance(valvar, pgast.TupleVar):
                valvar = pgast.ImplicitRowExpr(
                    args=[e.val for e in valvar.elements])

            query.where_clause = astutils.extend_binop(
                query.where_clause, pgast.NullTest(arg=valvar, negated=True))

        # The ORDER BY clause
        query.sort_clause = clauses.compile_orderby_clause(stmt.orderby,
                                                           ctx=ctx)

        # The OFFSET clause
        query.limit_offset = clauses.compile_limit_offset_clause(stmt.offset,
                                                                 ctx=ctx)

        # The LIMIT clause
        query.limit_count = clauses.compile_limit_offset_clause(stmt.limit,
                                                                ctx=ctx)

        clauses.fini_stmt(query, ctx, parent_ctx)

    return query
コード例 #11
0
ファイル: expr.py プロジェクト: sthagen/edgedb
def compile_operator(expr: irast.OperatorCall, args: Sequence[pgast.BaseExpr],
                     *, ctx: context.CompilerContextLevel) -> pgast.BaseExpr:
    lexpr = rexpr = None
    result: Optional[pgast.BaseExpr] = None

    if expr.operator_kind is ql_ft.OperatorKind.Infix:
        lexpr, rexpr = args
    elif expr.operator_kind is ql_ft.OperatorKind.Prefix:
        rexpr = args[0]
    elif expr.operator_kind is ql_ft.OperatorKind.Postfix:
        lexpr = args[0]
    else:
        raise RuntimeError(f'unexpected operator kind: {expr.operator_kind!r}')

    str_func_name = str(expr.func_shortname)
    if ((str_func_name in {'std::=', 'std::!='}
         or str(expr.origin_name) in {'std::=', 'std::!='})
            and expr.args[0].expr.typeref is not None
            and irtyputils.is_object(expr.args[0].expr.typeref)
            and expr.args[1].expr.typeref is not None
            and irtyputils.is_object(expr.args[1].expr.typeref)):
        if str_func_name == 'std::=' or str(expr.origin_name) == 'std::=':
            sql_oper = '='
        else:
            sql_oper = '!='

    elif str_func_name == 'std::EXISTS':
        result = pgast.NullTest(arg=rexpr, negated=True)

    elif expr.sql_operator:
        sql_oper = expr.sql_operator[0]
        if len(expr.sql_operator) > 1:
            # Explicit operand types given in FROM SQL OPERATOR
            if lexpr is not None:
                lexpr = pgast.TypeCast(
                    arg=lexpr,
                    type_name=pgast.TypeName(name=(expr.sql_operator[1], )))

            if rexpr is not None:
                rexpr_qry = None

                if (isinstance(rexpr, pgast.SubLink)
                        and isinstance(rexpr.expr, pgast.SelectStmt)):
                    rexpr_qry = rexpr.expr
                elif isinstance(rexpr, pgast.SelectStmt):
                    rexpr_qry = rexpr

                if rexpr_qry is not None:
                    # Handle cases like foo <op> ANY (SELECT) and
                    # foo <OP> (SELECT).
                    rexpr_qry.target_list[0] = pgast.ResTarget(
                        name=rexpr_qry.target_list[0].name,
                        val=pgast.TypeCast(arg=rexpr_qry.target_list[0].val,
                                           type_name=pgast.TypeName(
                                               name=(expr.sql_operator[2], ))))
                else:
                    rexpr = pgast.TypeCast(arg=rexpr,
                                           type_name=pgast.TypeName(
                                               name=(expr.sql_operator[2], )))

    elif expr.origin_name is not None:
        sql_oper = common.get_operator_backend_name(expr.origin_name)[1]

    else:
        sql_oper = common.get_operator_backend_name(expr.func_shortname)[1]

    # If result was not already computed, it's going to be a generic Expr.
    if result is None:
        result = pgast.Expr(
            kind=pgast.ExprKind.OP,
            name=sql_oper,
            lexpr=lexpr,
            rexpr=rexpr,
        )

    if expr.force_return_cast:
        # The underlying operator has a return value type
        # different from that of the EdgeQL operator declaration,
        # so we need to make an explicit cast here.
        result = pgast.TypeCast(
            arg=result,
            type_name=pgast.TypeName(
                name=pg_types.pg_type_from_ir_typeref(expr.typeref)))

    return result
コード例 #12
0
ファイル: shapecomp.py プロジェクト: stjordanis/edgedb
def compile_shape(ir_set: irast.Set, shape: Sequence[Tuple[irast.Set,
                                                           qlast.ShapeOp]], *,
                  ctx: context.CompilerContextLevel) -> pgast.TupleVar:
    elements = []

    # If the object identity is potentially nullable, filter it out
    # to prevent shapes with bogusly null insides.
    var = pathctx.get_path_value_var(ctx.rel,
                                     path_id=ir_set.path_id,
                                     env=ctx.env)
    if var.nullable:
        ctx.rel.where_clause = astutils.extend_binop(
            ctx.rel.where_clause, pgast.NullTest(arg=var, negated=True))

    with ctx.newscope() as shapectx:
        shapectx.disable_semi_join |= {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 = rptr.dir_cardinality
            is_singleton = (card.is_single() and
                            (not ctx.materializing 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)
コード例 #13
0
ファイル: output.py プロジェクト: stjordanis/edgedb
def named_tuple_as_json_object(
    expr: pgast.BaseExpr,
    *,
    styperef: irast.TypeRef,
    env: context.Environment,
) -> pgast.BaseExpr:
    keyvals: List[pgast.BaseExpr] = []

    if irtyputils.is_persistent_tuple(styperef):
        for el_type in styperef.subtypes:
            assert el_type.element_name
            keyvals.append(pgast.StringConstant(val=el_type.element_name))
            val: pgast.BaseExpr = pgast.Indirection(
                arg=expr,
                indirection=[pgast.ColumnRef(name=[el_type.element_name])])
            val = serialize_expr_to_json(val,
                                         styperef=el_type,
                                         nested=True,
                                         env=env)
            keyvals.append(val)

        obj = _build_json(
            'build_object',
            args=keyvals,
            null_safe=True,
            ser_safe=True,
            nullable=expr.nullable,
            env=env,
        )

    else:
        coldeflist = []

        for el_type in styperef.subtypes:
            assert el_type.element_name
            keyvals.append(pgast.StringConstant(val=el_type.element_name))

            coldeflist.append(
                pgast.ColumnDef(
                    name=el_type.element_name,
                    typename=pgast.TypeName(
                        name=pgtypes.pg_type_from_ir_typeref(el_type), ),
                ))

            val = pgast.ColumnRef(name=[el_type.element_name])

            val = serialize_expr_to_json(val,
                                         styperef=el_type,
                                         nested=True,
                                         env=env)

            keyvals.append(val)

        obj = _build_json(
            'build_object',
            args=keyvals,
            null_safe=True,
            ser_safe=True,
            nullable=expr.nullable,
            env=env,
        )

        obj = pgast.SelectStmt(
            target_list=[
                pgast.ResTarget(val=obj, ),
            ],
            from_clause=[
                pgast.RangeFunction(functions=[
                    pgast.FuncCall(
                        name=('unnest', ),
                        args=[pgast.ArrayExpr(elements=[expr], )],
                        coldeflist=coldeflist,
                    )
                ])
            ] if styperef.subtypes else [])

    if expr.nullable:
        obj = pgast.SelectStmt(target_list=[pgast.ResTarget(val=obj)],
                               where_clause=pgast.NullTest(arg=expr,
                                                           negated=True))
    return obj