def try_fold_associative_binop( opcall: irast.OperatorCall, *, ctx: context.ContextLevel) -> typing.Optional[irast.Set]: # Let's check if we have (CONST + (OTHER_CONST + X)) # tree, which can be optimized to ((CONST + OTHER_CONST) + X) op = opcall.func_shortname my_const = opcall.args[0].expr other_binop = opcall.args[1].expr folded = None if isinstance(other_binop.expr, irast.BaseConstant): my_const, other_binop = other_binop, my_const if (isinstance(my_const.expr, irast.BaseConstant) and isinstance(other_binop.expr, irast.OperatorCall) and other_binop.expr.func_shortname == op and other_binop.expr.operator_kind is ft.OperatorKind.INFIX): other_const = other_binop.expr.args[0].expr other_binop_node = other_binop.expr.args[1].expr if isinstance(other_binop_node.expr, irast.BaseConstant): other_binop_node, other_const = \ other_const, other_binop_node if isinstance(other_const.expr, irast.BaseConstant): try: new_const = ireval.evaluate( irast.OperatorCall( args=[ irast.CallArg(expr=other_const, ), irast.CallArg(expr=my_const, ), ], func_module_id=opcall.func_module_id, func_shortname=op, func_polymorphic=opcall.func_polymorphic, func_sql_function=opcall.func_sql_function, sql_operator=opcall.sql_operator, force_return_cast=opcall.force_return_cast, operator_kind=opcall.operator_kind, params_typemods=opcall.params_typemods, context=opcall.context, typeref=opcall.typeref, typemod=opcall.typemod, ), schema=ctx.env.schema, ) except ireval.UnsupportedExpressionError: pass else: folded_binop = irast.OperatorCall( args=[ irast.CallArg(expr=setgen.ensure_set(new_const, ctx=ctx), ), irast.CallArg(expr=other_binop_node, ), ], func_module_id=opcall.func_module_id, func_shortname=op, func_polymorphic=opcall.func_polymorphic, func_sql_function=opcall.func_sql_function, sql_operator=opcall.sql_operator, force_return_cast=opcall.force_return_cast, operator_kind=opcall.operator_kind, params_typemods=opcall.params_typemods, context=opcall.context, typeref=opcall.typeref, typemod=opcall.typemod, ) folded = setgen.ensure_set(folded_binop, ctx=ctx) return folded
def finalize_args( bound_call: polyres.BoundCall, *, ctx: context.ContextLevel, ) -> typing.Tuple[typing.List[irast.CallArg], typing.List[ft.TypeModifier]]: args: typing.List[irast.CallArg] = [] typemods = [] for barg in bound_call.args: param = barg.param arg = barg.val if param is None: # defaults bitmask args.append(irast.CallArg(expr=arg)) typemods.append(ft.TypeModifier.SINGLETON) continue param_mod = param.get_typemod(ctx.env.schema) typemods.append(param_mod) if param_mod is not ft.TypeModifier.SET_OF: arg_scope = pathctx.get_set_scope(arg, ctx=ctx) param_shortname = param.get_shortname(ctx.env.schema) # Arg was wrapped for scope fencing purposes, # but that fence has been removed above, so unwrap it. orig_arg = arg arg = irutils.unwrap_set(arg) if (param_mod is ft.TypeModifier.OPTIONAL or param_shortname in bound_call.null_args): if arg_scope is not None: # Due to the construction of relgen, the (unfenced) # subscope is necessary to shield LHS paths from the outer # query to prevent path binding which may break OPTIONAL. branch = arg_scope.unfence() pathctx.register_set_in_scope(arg, ctx=ctx) pathctx.mark_path_as_optional(arg.path_id, ctx=ctx) if arg_scope is not None: pathctx.assign_set_scope(arg, branch, ctx=ctx) elif arg_scope is not None: arg_scope.collapse() if arg is orig_arg: pathctx.assign_set_scope(arg, None, ctx=ctx) paramtype = barg.param_type param_kind = param.get_kind(ctx.env.schema) if param_kind is ft.ParameterKind.VARIADIC: # For variadic params, paramtype would be array<T>, # and we need T to cast the arguments. assert isinstance(paramtype, s_types.Array) paramtype = list(paramtype.get_subtypes(ctx.env.schema))[0] val_material_type = barg.valtype.material_type(ctx.env.schema) param_material_type = paramtype.material_type(ctx.env.schema) # Check if we need to cast the argument value before passing # it to the callable. For tuples, we also check that the element # names match. compatible = ( val_material_type.issubclass(ctx.env.schema, param_material_type) and (not param_material_type.is_tuple() or (param_material_type.get_element_names(ctx.env.schema) == val_material_type.get_element_names(ctx.env.schema)))) if not compatible: # The callable form was chosen via an implicit cast, # cast the arguments so that the backend has no # wiggle room to apply its own (potentially different) # casting. arg = cast.compile_cast(arg, paramtype, srcctx=None, ctx=ctx) if param_mod is not ft.TypeModifier.SET_OF: call_arg = irast.CallArg(expr=arg, cardinality=ft.Cardinality.ONE) else: call_arg = irast.CallArg(expr=arg, cardinality=None) stmtctx.get_expr_cardinality_later(target=call_arg, field='cardinality', irexpr=arg, ctx=ctx) args.append(call_arg) return args, typemods
def finalize_args( bound_call: polyres.BoundCall, *, actual_typemods: Sequence[ft.TypeModifier] = (), is_polymorphic: bool = False, ctx: context.ContextLevel, ) -> Tuple[List[irast.CallArg], List[ft.TypeModifier]]: args: List[irast.CallArg] = [] typemods = [] for i, barg in enumerate(bound_call.args): param = barg.param arg = barg.val if param is None: # defaults bitmask args.append(irast.CallArg(expr=arg)) typemods.append(ft.TypeModifier.SINGLETON) continue if actual_typemods: param_mod = actual_typemods[i] else: param_mod = param.get_typemod(ctx.env.schema) typemods.append(param_mod) if param_mod is not ft.TypeModifier.SET_OF: arg_scope = pathctx.get_set_scope(arg, ctx=ctx) param_shortname = param.get_parameter_name(ctx.env.schema) # Arg was wrapped for scope fencing purposes, # but that fence has been removed above, so unwrap it. orig_arg = arg arg = irutils.unwrap_set(arg) if (param_mod is ft.TypeModifier.OPTIONAL or param_shortname in bound_call.null_args): if arg_scope is not None: # Due to the construction of relgen, the (unfenced) # subscope is necessary to shield LHS paths from the outer # query to prevent path binding which may break OPTIONAL. branch = arg_scope.unfence() pathctx.register_set_in_scope(arg, ctx=ctx) pathctx.mark_path_as_optional(arg.path_id, ctx=ctx) if arg_scope is not None: pathctx.assign_set_scope(arg, branch, ctx=ctx) elif arg_scope is not None: arg_scope.collapse() if arg is orig_arg: pathctx.assign_set_scope(arg, None, ctx=ctx) else: if (is_polymorphic and ctx.expr_exposed and ctx.implicit_limit and isinstance(arg.expr, irast.SelectStmt) and arg.expr.limit is None): arg.expr.limit = setgen.ensure_set( dispatch.compile( qlast.IntegerConstant(value=str(ctx.implicit_limit)), ctx=ctx, ), ctx=ctx, ) paramtype = barg.param_type param_kind = param.get_kind(ctx.env.schema) if param_kind is ft.ParameterKind.VARIADIC: # For variadic params, paramtype would be array<T>, # and we need T to cast the arguments. assert isinstance(paramtype, s_types.Array) paramtype = list(paramtype.get_subtypes(ctx.env.schema))[0] # Check if we need to cast the argument value before passing # it to the callable. compatible = schemactx.is_type_compatible( paramtype, barg.valtype, ctx=ctx, ) if not compatible: # The callable form was chosen via an implicit cast, # cast the arguments so that the backend has no # wiggle room to apply its own (potentially different) # casting. arg = casts.compile_cast( arg, paramtype, srcctx=None, ctx=ctx) call_arg = irast.CallArg(expr=arg, cardinality=None) stmtctx.get_expr_cardinality_later( target=call_arg, field='cardinality', irexpr=arg, ctx=ctx) args.append(call_arg) return args, typemods
def finalize_args( bound_call: polyres.BoundCall, *, arg_ctxs: Dict[irast.Set, context.ContextLevel], actual_typemods: Sequence[ft.TypeModifier] = (), is_polymorphic: bool = False, ctx: context.ContextLevel, ) -> Tuple[List[irast.CallArg], List[ft.TypeModifier]]: args: List[irast.CallArg] = [] typemods = [] for i, barg in enumerate(bound_call.args): param = barg.param arg = barg.val if param is None: # defaults bitmask args.append(irast.CallArg(expr=arg)) typemods.append(ft.TypeModifier.SingletonType) continue if actual_typemods: param_mod = actual_typemods[i] else: param_mod = param.get_typemod(ctx.env.schema) typemods.append(param_mod) orig_arg = arg arg_ctx = arg_ctxs.get(orig_arg) arg_scope = pathctx.get_set_scope(arg, ctx=ctx) if param_mod is not ft.TypeModifier.SetOfType: param_shortname = param.get_parameter_name(ctx.env.schema) # Arg was wrapped for scope fencing purposes, # but that fence has been removed above, so unwrap it. arg = irutils.unwrap_set(arg) if (param_mod is ft.TypeModifier.OptionalType or param_shortname in bound_call.null_args): process_path_log(arg_ctx, arg_scope) if arg_scope is not None: # Due to the construction of relgen, the (unfenced) # subscope is necessary to shield LHS paths from the outer # query to prevent path binding which may break OPTIONAL. arg_scope.mark_as_optional() branch = arg_scope.unfence() pathctx.register_set_in_scope(arg, optional=True, ctx=ctx) if arg_scope is not None: pathctx.assign_set_scope(arg, branch, ctx=ctx) elif arg_scope is not None: arg_scope.collapse() if arg is orig_arg: pathctx.assign_set_scope(arg, None, ctx=ctx) else: process_path_log(arg_ctx, arg_scope) is_array_agg = ( isinstance(bound_call.func, s_func.Function) and ( bound_call.func.get_shortname(ctx.env.schema) == sn.QualName('std', 'array_agg') ) ) if ( # Ideally, we should implicitly slice all array values, # but in practice, the vast majority of large arrays # will come from array_agg, and so we only care about # that. is_array_agg and ctx.expr_exposed and ctx.implicit_limit and isinstance(arg.expr, irast.SelectStmt) and arg.expr.limit is None and not ctx.inhibit_implicit_limit ): arg.expr.limit = setgen.ensure_set( dispatch.compile( qlast.IntegerConstant(value=str(ctx.implicit_limit)), ctx=ctx, ), ctx=ctx, ) paramtype = barg.param_type param_kind = param.get_kind(ctx.env.schema) if param_kind is ft.ParameterKind.VariadicParam: # For variadic params, paramtype would be array<T>, # and we need T to cast the arguments. assert isinstance(paramtype, s_types.Array) paramtype = list(paramtype.get_subtypes(ctx.env.schema))[0] # Check if we need to cast the argument value before passing # it to the callable. compatible = s_types.is_type_compatible( paramtype, barg.valtype, schema=ctx.env.schema, ) if not compatible: # The callable form was chosen via an implicit cast, # cast the arguments so that the backend has no # wiggle room to apply its own (potentially different) # casting. arg = casts.compile_cast( arg, paramtype, srcctx=None, ctx=ctx) args.append(irast.CallArg(expr=arg, cardinality=None)) # If we have any logged paths left over and our enclosing # context is logging paths, propagate them up. if arg_ctx and arg_ctx.path_log and ctx.path_log is not None: ctx.path_log.extend(arg_ctx.path_log) return args, typemods
def finalize_args( bound_call: polyres.BoundCall, *, actual_typemods: Sequence[ft.TypeModifier] = (), guessed_typemods: Dict[Union[int, str], ft.TypeModifier], is_polymorphic: bool = False, ctx: context.ContextLevel, ) -> Tuple[List[irast.CallArg], List[ft.TypeModifier]]: args: List[irast.CallArg] = [] typemods = [] for i, barg in enumerate(bound_call.args): param = barg.param arg = barg.val arg_type_path_id: Optional[irast.PathId] = None if param is None: # defaults bitmask args.append(irast.CallArg(expr=arg)) typemods.append(ft.TypeModifier.SingletonType) continue if actual_typemods: param_mod = actual_typemods[i] else: param_mod = param.get_typemod(ctx.env.schema) typemods.append(param_mod) if param_mod is not ft.TypeModifier.SetOfType: param_shortname = param.get_parameter_name(ctx.env.schema) if param_shortname in bound_call.null_args: pathctx.register_set_in_scope(arg, optional=True, ctx=ctx) # If we guessed the argument was optional but it wasn't, # try to go back and make it *not* optional. elif (param_mod is ft.TypeModifier.SingletonType and barg.arg_id is not None and param_mod is not guessed_typemods[barg.arg_id]): for child in ctx.path_scope.children: if (child.path_id == arg.path_id or (arg.path_scope_id is not None and child.unique_id == arg.path_scope_id)): child.optional = False # Object type arguments to functions may be overloaded, so # we populate a path id field so that we can also pass the # type as an argument on the pgsql side. If the param type # is "anytype", though, then it can't be overloaded on # that argument. arg_type = setgen.get_set_type(arg, ctx=ctx) if (isinstance(arg_type, s_objtypes.ObjectType) and barg.param and not barg.param.get_type(ctx.env.schema).is_any( ctx.env.schema)): arg_type_path_id = pathctx.extend_path_id( arg.path_id, ptrcls=arg_type.getptr(ctx.env.schema, sn.UnqualName('__type__')), ctx=ctx, ) else: is_array_agg = (isinstance(bound_call.func, s_func.Function) and (bound_call.func.get_shortname(ctx.env.schema) == sn.QualName('std', 'array_agg'))) if ( # Ideally, we should implicitly slice all array values, # but in practice, the vast majority of large arrays # will come from array_agg, and so we only care about # that. is_array_agg and ctx.expr_exposed and ctx.implicit_limit and isinstance(arg.expr, irast.SelectStmt) and arg.expr.limit is None and not ctx.inhibit_implicit_limit): arg.expr.limit = dispatch.compile( qlast.IntegerConstant(value=str(ctx.implicit_limit)), ctx=ctx, ) paramtype = barg.param_type param_kind = param.get_kind(ctx.env.schema) if param_kind is ft.ParameterKind.VariadicParam: # For variadic params, paramtype would be array<T>, # and we need T to cast the arguments. assert isinstance(paramtype, s_types.Array) paramtype = list(paramtype.get_subtypes(ctx.env.schema))[0] # Check if we need to cast the argument value before passing # it to the callable. compatible = s_types.is_type_compatible( paramtype, barg.valtype, schema=ctx.env.schema, ) if not compatible: # The callable form was chosen via an implicit cast, # cast the arguments so that the backend has no # wiggle room to apply its own (potentially different) # casting. arg = casts.compile_cast(arg, paramtype, srcctx=None, ctx=ctx) args.append( irast.CallArg(expr=arg, expr_type_path_id=arg_type_path_id, is_default=barg.is_default)) return args, typemods