Пример #1
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
Пример #2
0
def new_unop(op, expr):
    return pgast.Expr(
        kind=pgast.ExprKind.OP,
        name=op,
        rexpr=expr
    )
Пример #3
0
def new_unop(op: str, expr: pgast.BaseExpr) -> pgast.Expr:
    return pgast.Expr(kind=pgast.ExprKind.OP, name=op, rexpr=expr)
Пример #4
0
def compile_OperatorCall(expr: irast.OperatorCall, *,
                         ctx: context.CompilerContextLevel) -> pgast.BaseExpr:

    if (expr.func_shortname == 'std::IF'
            and expr.args[0].cardinality is ql_ft.Cardinality.ONE
            and expr.args[2].cardinality is ql_ft.Cardinality.ONE):
        if_expr, condition, else_expr = (a.expr for a in expr.args)
        return pgast.CaseExpr(args=[
            pgast.CaseWhen(expr=dispatch.compile(condition, ctx=ctx),
                           result=dispatch.compile(if_expr, ctx=ctx))
        ],
                              defresult=dispatch.compile(else_expr, ctx=ctx))

    if expr.typemod is ql_ft.TypeModifier.SET_OF:
        raise RuntimeError(
            f'set returning operator {expr.func_shortname!r} is not supported '
            f'in simple expressions')

    args = [dispatch.compile(a.expr, ctx=ctx) for a in expr.args]
    lexpr = rexpr = 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}')

    if (expr.func_shortname == '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)):
        sql_oper = '='

    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 = pgast.TypeCast(
                    arg=rexpr,
                    type_name=pgast.TypeName(name=(expr.sql_operator[2], )))
    else:
        sql_oper = common.get_operator_backend_name(expr.func_shortname,
                                                    expr.func_module_id)[1]

    result: pgast.BaseExpr = 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
Пример #5
0
def new_binop(lexpr, rexpr, op):
    return pgast.Expr(kind=pgast.ExprKind.OP,
                      name=op,
                      lexpr=lexpr,
                      rexpr=rexpr)
Пример #6
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':
        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
Пример #7
0
def _compile_grouping_value(
        stmt: irast.GroupStmt, used_args: AbstractSet[str], *,
        ctx: context.CompilerContextLevel) -> pgast.BaseExpr:
    '''Produce the value for the grouping binding saying what is grouped on'''
    assert stmt.grouping_binding
    grouprel = ctx.rel

    # If there is only one thing grouped on, just output the hardcoded
    if len(used_args) == 1:
        return pgast.ArrayExpr(elements=[
            pgast.StringConstant(
                val=desugar_group.key_name(list(used_args)[0]))
        ])

    using = {k: stmt.using[k] for k in used_args}

    args = [
        pathctx.get_path_var(grouprel,
                             alias_set.path_id,
                             aspect='value',
                             env=ctx.env) for alias_set, _ in using.values()
    ]

    # Call grouping on each element we group on to produce a bitmask
    grouping_alias = ctx.env.aliases.get('g')
    grouping_call = pgast.FuncCall(name=('grouping', ), args=args)
    subq = pgast.SelectStmt(target_list=[
        pgast.ResTarget(name=grouping_alias, val=grouping_call),
    ])
    q = pgast.SelectStmt(from_clause=[
        pgast.RangeSubselect(
            subquery=subq, alias=pgast.Alias(aliasname=ctx.env.aliases.get()))
    ])

    grouping_ref = pgast.ColumnRef(name=(grouping_alias, ))

    # Generate a call to ARRAY[...] with a case for each grouping
    # element, then array_remove out the NULLs.
    els: List[pgast.BaseExpr] = []
    for i, name in enumerate(using):
        name = desugar_group.key_name(name)
        mask = 1 << (len(using) - i - 1)
        # (CASE (e & <mask>) WHEN 0 THEN '<name>' ELSE NULL END)

        els.append(
            pgast.CaseExpr(
                arg=pgast.Expr(kind=pgast.ExprKind.OP,
                               name='&',
                               lexpr=grouping_ref,
                               rexpr=pgast.LiteralExpr(expr=str(mask))),
                args=[
                    pgast.CaseWhen(expr=pgast.LiteralExpr(expr='0'),
                                   result=pgast.StringConstant(val=name))
                ],
                defresult=pgast.NullConstant()))

    val = pgast.FuncCall(
        name=('array_remove', ),
        args=[pgast.ArrayExpr(elements=els),
              pgast.NullConstant()])

    q.target_list.append(pgast.ResTarget(val=val))

    return q