in sources/cg_c.c [6259:6364]
static void cg_user_func(ast_node *ast, charbuf *is_null, charbuf *value) {
Contract(is_ast_call(ast));
EXTRACT_ANY_NOTNULL(name_ast, ast->left);
EXTRACT_STRING(name, name_ast);
EXTRACT_NOTNULL(call_arg_list, ast->right);
EXTRACT(arg_list, call_arg_list->right);
ast_node *params = NULL;
ast_node *func_stmt = find_func(name);
CSTR func_name = NULL;
bool_t proc_as_func = 0;
bool_t dml_proc = 0;
if (func_stmt) {
EXTRACT_STRING(fname, func_stmt->left);
params = get_func_params(func_stmt);
func_name = fname;
}
else {
// has to be one of these two, already validated
ast_node *proc_stmt = find_proc(name);
Invariant(proc_stmt);
params = get_proc_params(proc_stmt);
ast_node *proc_name_ast = get_proc_name(proc_stmt);
EXTRACT_STRING(pname, proc_name_ast);
func_name = pname;
proc_as_func = 1;
dml_proc = is_dml_proc(proc_stmt->sem->sem_type);
}
sem_t sem_type_result = ast->sem->sem_type;
// The answer will be stored in this scratch variable, any type is possible
CG_SETUP_RESULT_VAR(ast, sem_type_result);
CHARBUF_OPEN(invocation);
CG_CHARBUF_OPEN_SYM(func_sym, func_name);
if (dml_proc) {
// at least one arg for the out arg so add _db_ with comma
bprintf(&invocation, "_rc_ = %s(_db_, ", func_sym.ptr);
}
else {
bprintf(&invocation, "%s(", func_sym.ptr);
}
ast_node *item;
for (item = arg_list; item; item = item->right, params = params->right) {
EXTRACT_ANY(arg, item->left);
sem_t sem_type_arg = arg->sem->sem_type;
EXTRACT_NOTNULL(param, params->left);
sem_t sem_type_param = param->sem->sem_type;
cg_emit_one_arg(arg, sem_type_param, sem_type_arg, &invocation);
// if any more items in the list or the out arg still pending, we need comma
if (item->right || proc_as_func) {
bprintf(&invocation, ", ");
}
}
if (!item && params) {
// The only way this happens is when calling a stored proc like a function
// using the last arg as the return type.
Invariant(proc_as_func);
Invariant(!params->right);
EXTRACT_NOTNULL(param, params->left);
sem_t param_type = param->sem->sem_type;
Invariant(is_out_parameter(param_type));
Invariant(!is_in_parameter(param_type));
// the result variable is not an in/out arg, it's just a regular local
// it's otherwise the same as the paramater by consruction
sem_t arg_type = param_type & sem_not(SEM_TYPE_OUT_PARAMETER|SEM_TYPE_IN_PARAMETER);
cg_release_out_arg_before_call(arg_type, param_type, result_var.ptr);
bprintf(&invocation, "&%s", result_var.ptr);
}
bprintf(&invocation, ")");
// Now store the result of the call.
// the only trick here is we have to make sure we honor create semantics
// otherwise we can just copy the data since the variable is for sure
// an exact match for the call return by construction.
if (proc_as_func) {
// just do the function call, the result variable assignment happens as part of the call
bprintf(cg_main_output, "%s;\n", invocation.ptr);
if (dml_proc) {
// cascade the failure
cg_error_on_not_sqlite_ok();
}
}
else if (is_create_func(func_stmt->sem->sem_type)) {
cg_copy_for_create(cg_main_output, result_var.ptr, func_stmt->sem->sem_type, invocation.ptr);
}
else {
cg_copy(cg_main_output, result_var.ptr, func_stmt->sem->sem_type, invocation.ptr);
}
CHARBUF_CLOSE(func_sym);
CHARBUF_CLOSE(invocation);
CG_CLEANUP_RESULT_VAR(); // this will restore the scratch stack for us
}