Beispiel #1
0
def compile_FunctionCall(expr: irast.FunctionCall, *,
                         ctx: context.CompilerContextLevel) -> pgast.BaseExpr:

    if expr.typemod is ql_ft.TypeModifier.SetOfType:
        raise errors.UnsupportedFeatureError(
            'set returning functions are not supported in simple expressions')

    args = _compile_call_args(expr, ctx=ctx)

    if expr.has_empty_variadic and expr.variadic_param_type is not None:
        var = pgast.TypeCast(
            arg=pgast.ArrayExpr(elements=[]),
            type_name=pgast.TypeName(name=pg_types.pg_type_from_ir_typeref(
                expr.variadic_param_type)))

        args.append(pgast.VariadicArgument(expr=var))

    name = relgen.get_func_call_backend_name(expr, ctx=ctx)

    result: pgast.BaseExpr = pgast.FuncCall(name=name, args=args)

    if expr.force_return_cast:
        # The underlying function has a return value type
        # different from that of the EdgeQL function 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
Beispiel #2
0
def compile_FunctionCall(expr: irast.FunctionCall, *,
                         ctx: context.CompilerContextLevel) -> pgast.BaseExpr:

    if expr.typemod is ql_ft.TypeModifier.SET_OF:
        raise RuntimeError(
            'set returning functions are not supported in simple expressions')

    args = [dispatch.compile(a.expr, ctx=ctx) for a in expr.args]

    if expr.has_empty_variadic and expr.variadic_param_type is not None:
        var = pgast.TypeCast(
            arg=pgast.ArrayExpr(elements=[]),
            type_name=pgast.TypeName(name=pg_types.pg_type_from_ir_typeref(
                expr.variadic_param_type)))

        args.append(pgast.VariadicArgument(expr=var))

    if expr.func_sql_function:
        name = (expr.func_sql_function, )
    else:
        name = common.get_function_backend_name(expr.func_shortname,
                                                expr.func_module_id)

    result: pgast.BaseExpr = pgast.FuncCall(name=name, args=args)

    if expr.force_return_cast:
        # The underlying function has a return value type
        # different from that of the EdgeQL function 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
Beispiel #3
0
def compile_TypeCheckOp(expr: irast.TypeCheckOp, *,
                        ctx: context.CompilerContextLevel) -> pgast.BaseExpr:

    with ctx.new() as newctx:
        newctx.expr_exposed = False
        left = dispatch.compile(expr.left, ctx=newctx)
        negated = expr.op == 'IS NOT'

        result: pgast.BaseExpr

        if expr.result is not None:
            result = pgast.BooleanConstant(
                val='false' if not expr.result or negated else 'true')
        else:
            right: pgast.BaseExpr

            if expr.right.union:
                right = pgast.ArrayExpr(elements=[
                    dispatch.compile(c, ctx=newctx) for c in expr.right.union
                ])
            else:
                right = dispatch.compile(expr.right, ctx=newctx)

            result = pgast.FuncCall(name=('edgedb', 'issubclass'),
                                    args=[left, right])

            if negated:
                result = astutils.new_unop('NOT', result)

    return result
Beispiel #4
0
def unnamed_tuple_as_json_object(expr, *, styperef, env):
    vals = []

    if styperef.in_schema:
        for el_idx, el_type in enumerate(styperef.subtypes):
            val = pgast.Indirection(
                arg=expr,
                indirection=[
                    pgast.ColumnRef(name=[str(el_idx)], ),
                ],
            )
            if irtyputils.is_collection(el_type):
                val = coll_as_json_object(val, styperef=el_type, env=env)
            vals.append(val)

        return pgast.FuncCall(name=_get_json_func('build_array', env=env),
                              args=vals,
                              null_safe=True,
                              ser_safe=True,
                              nullable=expr.nullable)

    else:
        coldeflist = []

        for el_idx, el_type in enumerate(styperef.subtypes):

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

            val = pgast.ColumnRef(name=[str(el_idx)])

            if irtyputils.is_collection(el_type):
                val = coll_as_json_object(val, styperef=el_type, env=env)

            vals.append(val)

        res = pgast.FuncCall(name=_get_json_func('build_array', env=env),
                             args=vals,
                             null_safe=True,
                             ser_safe=True,
                             nullable=expr.nullable)

        return pgast.SelectStmt(
            target_list=[
                pgast.ResTarget(val=res, ),
            ],
            from_clause=[
                pgast.RangeFunction(functions=[
                    pgast.FuncCall(
                        name=('unnest', ),
                        args=[pgast.ArrayExpr(elements=[expr], )],
                        coldeflist=coldeflist,
                    )
                ])
            ])
Beispiel #5
0
def tuple_getattr(tuple_val, tuple_typeref, attr):
    ttypes = []
    pgtypes = []
    for i, st in enumerate(tuple_typeref.subtypes):
        pgtype = pg_types.pg_type_from_ir_typeref(st)
        pgtypes.append(pgtype)

        if st.element_name:
            ttypes.append(st.element_name)
        else:
            ttypes.append(str(i))

    index = ttypes.index(attr)

    if tuple_typeref.in_schema:
        set_expr = pgast.Indirection(
            arg=tuple_val,
            indirection=[
                pgast.ColumnRef(
                    name=[attr],
                ),
            ],
        )
    else:
        set_expr = pgast.SelectStmt(
            target_list=[
                pgast.ResTarget(
                    val=pgast.ColumnRef(
                        name=[str(index)],
                    ),
                ),
            ],
            from_clause=[
                pgast.RangeFunction(
                    functions=[
                        pgast.FuncCall(
                            name=('unnest',),
                            args=[
                                pgast.ArrayExpr(
                                    elements=[tuple_val],
                                )
                            ],
                            coldeflist=[
                                pgast.ColumnDef(
                                    name=str(i),
                                    typename=pgast.TypeName(
                                        name=t
                                    )
                                )
                                for i, t in enumerate(pgtypes)
                            ]
                        )
                    ]
                )
            ]
        )

    return set_expr
Beispiel #6
0
def safe_array_expr(elements: typing.List[pgast.Base], **kwargs) -> pgast.Base:
    result = pgast.ArrayExpr(elements=elements, **kwargs)
    if any(el.nullable for el in elements):
        result = pgast.FuncCall(
            name=('edgedb', '_nullif_array_nulls'),
            args=[result],
            **kwargs,
        )
    return result
Beispiel #7
0
def safe_array_expr(
    elements: List[pgast.BaseExpr],
    *,
    ser_safe: bool = False,
) -> pgast.BaseExpr:
    result: pgast.BaseExpr = pgast.ArrayExpr(
        elements=elements,
        ser_safe=ser_safe,
    )
    if any(el.nullable for el in elements):
        result = pgast.FuncCall(
            name=('edgedb', '_nullif_array_nulls'),
            args=[result],
            ser_safe=ser_safe,
        )
    return result
Beispiel #8
0
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:
            keyvals.append(pgast.StringConstant(val=el_type.element_name))
            val: pgast.BaseExpr = pgast.Indirection(
                arg=expr,
                indirection=[
                    pgast.ColumnRef(
                        name=[el_type.element_name]
                    )
                ]
            )
            if irtyputils.is_collection(el_type):
                val = coll_as_json_object(val, styperef=el_type, env=env)
            keyvals.append(val)

        return pgast.FuncCall(
            name=_get_json_func('build_object', env=env),
            args=keyvals, null_safe=True, ser_safe=True,
            nullable=expr.nullable)

    else:
        coldeflist = []

        for el_type in styperef.subtypes:
            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])

            if irtyputils.is_collection(el_type):
                val = coll_as_json_object(val, styperef=el_type, env=env)

            keyvals.append(val)

        res = pgast.FuncCall(
            name=_get_json_func('build_object', env=env),
            args=keyvals, null_safe=True, ser_safe=True,
            nullable=expr.nullable)

        return pgast.SelectStmt(
            target_list=[
                pgast.ResTarget(
                    val=res,
                ),
            ],
            from_clause=[
                pgast.RangeFunction(
                    functions=[
                        pgast.FuncCall(
                            name=('unnest',),
                            args=[
                                pgast.ArrayExpr(
                                    elements=[expr],
                                )
                            ],
                            coldeflist=coldeflist,
                        )
                    ]
                )
            ]
        )
Beispiel #9
0
def compile_ConfigSet(
    op: irast.ConfigSet,
    *,
    ctx: context.CompilerContextLevel,
) -> pgast.BaseExpr:

    val: pgast.BaseExpr

    with ctx.new() as subctx:
        if op.backend_setting:
            output_format = context.OutputFormat.NATIVE
        else:
            output_format = context.OutputFormat.JSONB

        with context.output_format(ctx, output_format):
            if isinstance(op.expr, irast.EmptySet):
                # Special handling for empty sets, because we want a
                # singleton representation of the value and not an empty rel
                # in this context.
                if op.cardinality is qltypes.SchemaCardinality.One:
                    val = pgast.NullConstant()
                elif subctx.env.output_format is context.OutputFormat.JSONB:
                    val = pgast.TypeCast(
                        arg=pgast.StringConstant(val='[]'),
                        type_name=pgast.TypeName(
                            name=('jsonb',),
                        ),
                    )
                else:
                    val = pgast.TypeCast(
                        arg=pgast.ArrayExpr(),
                        type_name=pgast.TypeName(
                            name=('text[]',),
                        ),
                    )
            else:
                val = dispatch.compile(op.expr, ctx=subctx)
                assert isinstance(val, pgast.SelectStmt), "expected SelectStmt"

                pathctx.get_path_serialized_output(
                    val, op.expr.path_id, env=ctx.env)

                if op.cardinality is qltypes.SchemaCardinality.Many:
                    val = output.aggregate_json_output(
                        val, op.expr, env=ctx.env)

    result: pgast.BaseExpr

    if op.scope is qltypes.ConfigScope.SYSTEM and op.backend_setting:
        assert isinstance(val, pgast.SelectStmt) and len(val.target_list) == 1
        valval = val.target_list[0].val
        if isinstance(valval, pgast.TypeCast):
            valval = valval.arg
        if not isinstance(valval, pgast.BaseConstant):
            raise AssertionError('value is not a constant in ConfigSet')
        result = pgast.AlterSystem(
            name=op.backend_setting,
            value=valval,
        )

    elif op.scope is qltypes.ConfigScope.DATABASE and op.backend_setting:
        fcall = pgast.FuncCall(
            name=('edgedb', '_alter_current_database_set'),
            args=[pgast.StringConstant(val=op.backend_setting), val],
        )

        result = output.wrap_script_stmt(
            pgast.SelectStmt(target_list=[pgast.ResTarget(val=fcall)]),
            suppress_all_output=True,
            env=ctx.env,
        )

    elif op.scope is qltypes.ConfigScope.SESSION and op.backend_setting:
        fcall = pgast.FuncCall(
            name=('pg_catalog', 'set_config'),
            args=[
                pgast.StringConstant(val=op.backend_setting),
                pgast.TypeCast(
                    arg=val,
                    type_name=pgast.TypeName(name=('text',)),
                ),
                pgast.BooleanConstant(val='false'),
            ],
        )

        result = output.wrap_script_stmt(
            pgast.SelectStmt(target_list=[pgast.ResTarget(val=fcall)]),
            suppress_all_output=True,
            env=ctx.env,
        )

    elif op.scope is qltypes.ConfigScope.SYSTEM:
        result_row = pgast.RowExpr(
            args=[
                pgast.StringConstant(val='SET'),
                pgast.StringConstant(val=str(op.scope)),
                pgast.StringConstant(val=op.name),
                val,
            ]
        )

        result = pgast.FuncCall(
            name=('jsonb_build_array',),
            args=result_row.args,
            null_safe=True,
            ser_safe=True,
        )

        result = pgast.SelectStmt(
            target_list=[
                pgast.ResTarget(
                    val=result,
                ),
            ],
        )
    elif op.scope is qltypes.ConfigScope.SESSION:
        result = pgast.InsertStmt(
            relation=pgast.RelRangeVar(
                relation=pgast.Relation(
                    name='_edgecon_state',
                ),
            ),
            select_stmt=pgast.SelectStmt(
                values=[
                    pgast.ImplicitRowExpr(
                        args=[
                            pgast.StringConstant(
                                val=op.name,
                            ),
                            val,
                            pgast.StringConstant(
                                val='C',
                            ),
                        ]
                    )
                ]
            ),
            cols=[
                pgast.ColumnRef(name=['name']),
                pgast.ColumnRef(name=['value']),
                pgast.ColumnRef(name=['type']),
            ],
            on_conflict=pgast.OnConflictClause(
                action='update',
                infer=pgast.InferClause(
                    index_elems=[
                        pgast.ColumnRef(name=['name']),
                        pgast.ColumnRef(name=['type']),
                    ],
                ),
                target_list=[
                    pgast.MultiAssignRef(
                        columns=[pgast.ColumnRef(name=['value'])],
                        source=pgast.RowExpr(
                            args=[
                                val,
                            ],
                        ),
                    ),
                ],
            ),
        )
    elif op.scope is qltypes.ConfigScope.DATABASE:
        result = pgast.InsertStmt(
            relation=pgast.RelRangeVar(
                relation=pgast.Relation(
                    name='_db_config',
                    schemaname='edgedb',
                ),
            ),
            select_stmt=pgast.SelectStmt(
                values=[
                    pgast.ImplicitRowExpr(
                        args=[
                            pgast.StringConstant(
                                val=op.name,
                            ),
                            val,
                        ]
                    )
                ]
            ),
            cols=[
                pgast.ColumnRef(name=['name']),
                pgast.ColumnRef(name=['value']),
            ],
            on_conflict=pgast.OnConflictClause(
                action='update',
                infer=pgast.InferClause(
                    index_elems=[
                        pgast.ColumnRef(name=['name']),
                    ],
                ),
                target_list=[
                    pgast.MultiAssignRef(
                        columns=[pgast.ColumnRef(name=['value'])],
                        source=pgast.RowExpr(
                            args=[
                                val,
                            ],
                        ),
                    ),
                ],
            ),
        )
    else:
        raise AssertionError(f'unexpected configuration scope: {op.scope}')

    return result
Beispiel #10
0
def unnamed_tuple_as_json_object(
    expr: pgast.BaseExpr,
    *,
    styperef: irast.TypeRef,
    env: context.Environment,
) -> pgast.BaseExpr:
    vals: List[pgast.BaseExpr] = []

    if irtyputils.is_persistent_tuple(styperef):
        for el_idx, el_type in enumerate(styperef.subtypes):
            val: pgast.BaseExpr = pgast.Indirection(
                arg=expr,
                indirection=[
                    pgast.ColumnRef(name=[str(el_idx)], ),
                ],
            )
            if irtyputils.is_collection(el_type):
                val = coll_as_json_object(val, styperef=el_type, env=env)
            vals.append(val)

        return _build_json(
            'build_array',
            args=vals,
            null_safe=True,
            ser_safe=True,
            nullable=expr.nullable,
            env=env,
        )

    else:
        coldeflist = []

        for el_idx, el_type in enumerate(styperef.subtypes):

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

            val = pgast.ColumnRef(name=[str(el_idx)])

            if irtyputils.is_collection(el_type):
                val = coll_as_json_object(val, styperef=el_type, env=env)

            vals.append(val)

        res = _build_json(
            'build_array',
            args=vals,
            null_safe=True,
            ser_safe=True,
            nullable=expr.nullable,
            env=env,
        )

        return pgast.SelectStmt(
            target_list=[
                pgast.ResTarget(val=res, ),
            ],
            from_clause=[
                pgast.RangeFunction(functions=[
                    pgast.FuncCall(
                        name=('unnest', ),
                        args=[pgast.ArrayExpr(elements=[expr], )],
                        coldeflist=coldeflist,
                    )
                ])
            ])
Beispiel #11
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
Beispiel #12
0
def _compile_config_value(
    op: irast.ConfigSet,
    *,
    ctx: context.CompilerContextLevel,
) -> pgast.BaseExpr:
    val: pgast.BaseExpr

    if op.backend_setting:
        assert op.backend_expr is not None
        expr = op.backend_expr
    else:
        expr = op.expr

    with ctx.new() as subctx:
        if op.backend_setting or op.scope == qltypes.ConfigScope.GLOBAL:
            output_format = context.OutputFormat.NATIVE
        else:
            output_format = context.OutputFormat.JSONB

        with context.output_format(ctx, output_format):
            if isinstance(expr, irast.EmptySet):
                # Special handling for empty sets, because we want a
                # singleton representation of the value and not an empty rel
                # in this context.
                if op.cardinality is qltypes.SchemaCardinality.One:
                    val = pgast.NullConstant()
                elif subctx.env.output_format is context.OutputFormat.JSONB:
                    val = pgast.TypeCast(
                        arg=pgast.StringConstant(val='[]'),
                        type_name=pgast.TypeName(name=('jsonb', ), ),
                    )
                else:
                    val = pgast.TypeCast(
                        arg=pgast.ArrayExpr(elements=[]),
                        type_name=pgast.TypeName(name=('text[]', ), ),
                    )
            else:
                val = dispatch.compile(expr, ctx=subctx)
                assert isinstance(val, pgast.SelectStmt), "expected SelectStmt"

                pathctx.get_path_serialized_output(val,
                                                   expr.path_id,
                                                   env=ctx.env)

                if op.cardinality is qltypes.SchemaCardinality.Many:
                    val = output.aggregate_json_output(val, expr, env=ctx.env)

    # For globals, we need to output the binary encoding so that we
    # can just hand it back to the server. We abuse `record_send` to
    # act as a generic `_send` function
    if op.scope is qltypes.ConfigScope.GLOBAL:
        val = pgast.FuncCall(
            name=('substring', ),
            args=[
                pgast.FuncCall(
                    name=('record_send', ),
                    args=[pgast.RowExpr(args=[val])],
                ),
                # The first twelve bytes are header, the rest is the
                # encoding of the actual element
                pgast.NumericConstant(val="13"),
            ],
        )
        cast_name = s_casts.get_cast_fullname_from_names(
            'std', 'std::bytes', 'std::json')
        val = pgast.FuncCall(
            name=common.get_cast_backend_name(cast_name, aspect='function'),
            args=[val],
        )

    if op.backend_setting and op.scope is qltypes.ConfigScope.INSTANCE:
        assert isinstance(val, pgast.SelectStmt) and len(val.target_list) == 1
        val = val.target_list[0].val
        if isinstance(val, pgast.TypeCast):
            val = val.arg
        if not isinstance(val, pgast.BaseConstant):
            raise AssertionError('value is not a constant in ConfigSet')

    return val
Beispiel #13
0
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