예제 #1
0
def rel_join(
        query: pgast.Query, right_rvar: pgast.BaseRangeVar, *,
        ctx: context.CompilerContextLevel) -> None:
    condition = None

    for path_id in right_rvar.path_scope:
        lref = maybe_get_path_var(query, path_id, aspect='identity', ctx=ctx)
        if lref is None:
            lref = maybe_get_path_var(query, path_id, aspect='value', ctx=ctx)
        if lref is None:
            continue

        rref = pathctx.get_rvar_path_identity_var(
            right_rvar, path_id, env=ctx.env)

        path_cond = astutils.join_condition(lref, rref)
        condition = astutils.extend_binop(condition, path_cond)

    if condition is None:
        join_type = 'cross'
    else:
        join_type = 'inner'

    if not query.from_clause:
        query.from_clause.append(right_rvar)
        if condition is not None:
            query.where_clause = astutils.extend_binop(
                query.where_clause, condition)
    else:
        larg = query.from_clause[0]
        rarg = right_rvar

        query.from_clause[0] = pgast.JoinExpr(
            type=join_type, larg=larg, rarg=rarg, quals=condition)
예제 #2
0
파일: pathctx.py 프로젝트: mcaramma/edgedb
def get_path_output_or_null(
        rel: pgast.Query, path_id: irast.PathId, *,
        aspect: str, env: context.Environment) -> \
        typing.Tuple[pgast.OutputVar, bool]:

    path_id = map_path_id(path_id, rel.view_path_id_map)

    ref = maybe_get_path_output(rel, path_id, aspect=aspect, env=env)
    if ref is not None:
        return ref, False

    alt_aspect = get_less_specific_aspect(path_id, aspect)
    if alt_aspect is not None:
        ref = maybe_get_path_output(rel, path_id, aspect=alt_aspect, env=env)
        if ref is not None:
            rel.path_outputs[path_id, aspect] = ref
            return ref, False

    alias = env.aliases.get('null')
    restarget = pgast.ResTarget(name=alias, val=pgast.Constant(val=None))

    if hasattr(rel, 'returning_list'):
        rel.returning_list.append(restarget)
    else:
        rel.target_list.append(restarget)

    ref = pgast.ColumnRef(name=[alias], nullable=True)
    rel.path_outputs[path_id, aspect] = ref

    return ref, True
예제 #3
0
파일: dml.py 프로젝트: versada/edgedb
def fini_dml_stmt(ir_stmt: irast.MutatingStmt, wrapper: pgast.Query,
                  dml_cte: pgast.CommonTableExpr, dml_rvar: pgast.BaseRangeVar,
                  *, parent_ctx: context.CompilerContextLevel,
                  ctx: context.CompilerContextLevel) -> pgast.Query:

    # Record the effect of this insertion in the relation overlay
    # context to ensure that the RETURNING clause potentially
    # referencing this class yields the expected results.
    if isinstance(ir_stmt, irast.InsertStmt):
        dbobj.add_rel_overlay(ir_stmt.subject.scls,
                              'union',
                              dml_cte,
                              env=ctx.env)
    elif isinstance(ir_stmt, irast.DeleteStmt):
        dbobj.add_rel_overlay(ir_stmt.subject.scls,
                              'except',
                              dml_cte,
                              env=ctx.env)

    if parent_ctx.toplevel_stmt is wrapper:
        ret_ref = pathctx.get_path_identity_var(wrapper,
                                                ir_stmt.subject.path_id,
                                                env=parent_ctx.env)
        count = pgast.FuncCall(name=('count', ), args=[ret_ref])
        wrapper.target_list = [pgast.ResTarget(val=count)]

    clauses.fini_stmt(wrapper, ctx, parent_ctx)

    return wrapper
예제 #4
0
파일: pathctx.py 프로젝트: mcaramma/edgedb
def get_path_serialized_output(rel: pgast.Query, path_id: irast.PathId, *,
                               env: context.Environment) -> pgast.OutputVar:
    # Serialized output is a special case, we don't
    # want this behaviour to be recursive, so it
    # must be kept outside of get_path_output() generic.
    aspect = 'serialized'

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

    ref = get_path_serialized_or_value_var(rel, path_id, env=env)

    ref = output.serialize_expr(ref, path_id=path_id, env=env)
    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)

    result = pgast.ColumnRef(name=[alias], nullable=ref.nullable)
    rel.path_outputs[path_id, aspect] = result
    return result
예제 #5
0
파일: relctx.py 프로젝트: mcaramma/edgedb
def semi_join(stmt: pgast.Query, ir_set: irast.Set,
              src_rvar: pgast.BaseRangeVar, *,
              ctx: context.CompilerContextLevel) -> pgast.BaseRangeVar:
    """Join an IR Set using semi-join."""
    rptr = ir_set.rptr
    ptrcls = rptr.ptrcls
    ptr_info = pg_types.get_pointer_storage_info(ptrcls,
                                                 resolve_type=False,
                                                 link_bias=False)
    is_inline_ref = ptr_info.table_type == 'ObjectType'

    # Target set range.
    set_rvar = new_root_rvar(ir_set, ctx=ctx)

    # Link range.
    map_rvar = new_pointer_rvar(rptr, src_rvar=src_rvar, ctx=ctx)

    # Target identity in the target range.
    if rptr.is_inbound and is_inline_ref:
        tgt_pid = ir_set.path_id.extend(ptrcls)
    else:
        tgt_pid = ir_set.path_id

    tgt_ref = pathctx.get_rvar_path_identity_var(set_rvar,
                                                 tgt_pid,
                                                 env=ctx.env)

    include_rvar(ctx.rel, map_rvar, path_id=ir_set.path_id.ptr_path(), ctx=ctx)

    pathctx.get_path_identity_output(ctx.rel, ir_set.path_id, env=ctx.env)

    cond = astutils.new_binop(tgt_ref, ctx.rel, 'IN')
    stmt.where_clause = astutils.extend_binop(stmt.where_clause, cond)

    return set_rvar
예제 #6
0
파일: output.py 프로젝트: versada/edgedb
def top_output_as_value(stmt: pgast.Query, *,
                        env: context.Environment) -> pgast.Query:
    """Finalize output serialization on the top level."""

    if env.output_format == context.OutputFormat.JSON:
        # For JSON we just want to aggregate the whole thing
        # into a JSON array.
        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.name = env.aliases.get('v')

        new_val = pgast.FuncCall(name=('json_agg', ),
                                 args=[pgast.ColumnRef(name=[stmt_res.name])])

        # XXX: nullability introspection is not reliable,
        #      remove `True or` once it is.
        if True or stmt_res.val.nullable:
            new_val = pgast.CoalesceExpr(
                args=[new_val, pgast.Constant(val='[]')])

        result = pgast.SelectStmt(target_list=[pgast.ResTarget(val=new_val)],
                                  from_clause=[subrvar])

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

        return result

    else:
        return stmt
예제 #7
0
파일: pathctx.py 프로젝트: mcaramma/edgedb
def put_path_var(rel: pgast.Query,
                 path_id: irast.PathId,
                 var: pgast.Base,
                 *,
                 aspect: str,
                 force: bool = False,
                 env: context.Environment) -> None:
    if (path_id, aspect) in rel.path_namespace and not force:
        raise KeyError(f'{aspect} of {path_id} is already present in {rel}')
    rel.path_namespace[path_id, aspect] = var
예제 #8
0
파일: pathctx.py 프로젝트: mcaramma/edgedb
def put_path_rvar(stmt: pgast.Query, path_id: irast.PathId,
                  rvar: pgast.BaseRangeVar, *, aspect: str,
                  env: context.Environment) -> None:
    assert isinstance(path_id, irast.PathId)
    stmt.path_rvar_map[path_id, aspect] = rvar

    # Normally, masked paths (i.e paths that are only behind a fence below),
    # will not be exposed in a query namespace.  However, when the masked
    # path in the *main* path of a set, it must still be exposed, but no
    # further than the immediate parent query.
    if path_id in rvar.query.path_id_mask:
        stmt.path_id_mask.add(path_id)
예제 #9
0
파일: clauses.py 프로젝트: mcaramma/edgedb
def fini_stmt(stmt: pgast.Query, ctx: context.CompilerContextLevel,
              parent_ctx: context.CompilerContextLevel) -> None:
    if stmt is ctx.toplevel_stmt:
        stmt.argnames = ctx.argmap