in sources/cg_c.c [6466:6597]
static void cg_call_stmt_with_cursor(ast_node *ast, CSTR cursor_name) {
Contract(is_ast_call_stmt(ast));
EXTRACT_ANY_NOTNULL(name_ast, ast->left);
EXTRACT_STRING(name, name_ast);
EXTRACT_ANY(expr_list, ast->right);
// check for call to unknown proc, use canonical calling convention for those
ast_node *proc_stmt = find_proc(name);
if (!proc_stmt) {
cg_call_external(ast);
return;
}
ast_node *proc_name_ast = get_proc_name(proc_stmt);
EXTRACT_STRING(proc_name, proc_name_ast);
ast_node *params = get_proc_params(proc_stmt);
bool_t dml_proc = is_dml_proc(proc_stmt->sem->sem_type);
bool_t result_set_proc = has_result_set(ast);
bool_t out_stmt_proc = has_out_stmt_result(ast);
bool_t out_union_proc = has_out_union_stmt_result(ast);
CSTR fetch_results = out_union_proc ? "_fetch_results" : "";
CHARBUF_OPEN(invocation);
CG_CHARBUF_OPEN_SYM(proc_sym, proc_name, fetch_results);
CG_CHARBUF_OPEN_SYM(result_type, proc_name, "_row");
CG_CHARBUF_OPEN_SYM(result_sym, proc_name, "_row", "_data");
CG_CHARBUF_OPEN_SYM(result_set_ref, name, "_result_set_ref");
// most cases will emit hidden arg, such as the db
bool_t prefix_args = true;
if (dml_proc) {
bprintf(&invocation, "_rc_ = %s(_db_", proc_sym.ptr);
if (out_union_proc && !cursor_name) {
// This is case 3b above. The tricky bit here is that there might
// be more than one such call. The callee is not going to release
// the out arg as it might be junk from the callee's perspective so
// we have to release it in case this call is in a loop or if this
// call is repeated in some other way
bprintf(cg_main_output, "cql_object_release(*_result_set_);\n");
bprintf(&invocation, ", (%s *)_result_set_", result_set_ref.ptr);
}
else if (out_union_proc) {
// this is case 3a above.
Invariant(cursor_name); // either specified or the default _result_ variable
bprintf(&invocation, ", &%s_result_set_", cursor_name);
}
else if (result_set_proc && cursor_name == NULL) {
// This is case 1b above, prop the result as our output. As with case
// 3b above we have to pre-release _result_stmt_ because of repetition.
bprintf(cg_main_output, "cql_finalize_stmt(_result_stmt);\n");
bprintf(&invocation, ", _result_stmt");
}
else if (result_set_proc) {
// this is case 1a above
Invariant(cursor_name); // either specified or the default _result_ variable
bprintf(&invocation, ", &%s_stmt", cursor_name);
}
}
else {
bprintf(&invocation, "%s(", proc_sym.ptr);
if (out_union_proc && !cursor_name) {
// this is 3b again, but with no database arg. As with case
// 3b above we have to pre-release _result_stmt_ because of repetition.
bprintf(cg_main_output, "cql_object_release(*_result_set_);\n");
bprintf(&invocation, "(%s *)_result_set_", result_set_ref.ptr);
}
else if (out_union_proc) {
// this is 3a again, but with no database arg
Invariant(cursor_name); // either specified or the default _result_ variable
bprintf(&invocation, "&%s_result_set_", cursor_name);
}
else {
// no prefix args were emitted (case 4b, with no DML)
prefix_args = false;
}
}
// if we emitted something (most cases) and there are args, we need a comma now
if (prefix_args && expr_list) {
bprintf(&invocation, ", ");
}
// we don't need to manage the stack, we're always called at the top level
// we're wiping it when we exit this function anyway
Invariant(stack_level == 0);
// emit provided args, the param specs are needed for possible type conversions
cg_emit_proc_params(&invocation, params, expr_list);
// For a fetch results proc we have to add the out argument here.
// Declare that variable if needed.
if (out_stmt_proc) {
// this is case 2a above
Invariant(cursor_name); // this would be 2b, not allowed(!)
if (dml_proc || params) {
bprintf(&invocation, ", ");
}
bprintf(cg_main_output, "cql_teardown_row(%s);\n", cursor_name);
bprintf(&invocation, "(%s *)&%s", result_type.ptr, cursor_name);
bprintf(&invocation, "); // %s identical to cursor type\n", result_type.ptr);
}
else {
bprintf(&invocation, ");\n");
}
bprintf(cg_main_output, "%s", invocation.ptr);
if (out_union_proc && cursor_name) {
// case 3a, capturing the cursor, we set the row index to -1 (it will be pre-incremented)
bprintf(cg_main_output, "%s_row_num_ = %s_row_count_ = -1;\n", cursor_name, cursor_name);
}
if (dml_proc) {
// if there is an error code, check it, and cascade the failure
cg_error_on_not_sqlite_ok();
}
if (out_union_proc && cursor_name) {
// case 3a again
bprintf(cg_main_output, "%s_row_count_ = cql_result_set_get_count((cql_result_set_ref)%s_result_set_);\n",
cursor_name, cursor_name);
}
CHARBUF_CLOSE(result_set_ref);
CHARBUF_CLOSE(result_sym);
CHARBUF_CLOSE(result_type);
CHARBUF_CLOSE(proc_sym);
CHARBUF_CLOSE(invocation);
}