in sources/cg_c.c [5300:5430]
static void cg_declare_cursor(ast_node *ast) {
Contract(is_ast_declare_cursor(ast));
EXTRACT_ANY_NOTNULL(name_ast, ast->left);
EXTRACT_STRING(cursor_name, name_ast);
bool_t out_union_proc = false;
bool_t is_boxed = !!(name_ast->sem->sem_type & SEM_TYPE_BOXED);
bool_t is_unboxing = is_ast_str(ast->right);
if (is_ast_call_stmt(ast->right)) {
out_union_proc = has_out_union_stmt_result(ast);
}
// only one of these (is boxed makes no sense with out union)
Invariant(!out_union_proc || !is_boxed);
// can't be both of these either
Invariant(!out_union_proc || !is_unboxing);
// unboxing implies is_boxed a->b <==> (!a | b)
Invariant(!is_unboxing || is_boxed);
if (out_union_proc) {
EXTRACT_STRING(name, ast->right->left);
CG_CHARBUF_OPEN_SYM(result_ref, name, "_result_set_ref");
bprintf(cg_declarations_output, "%s %s_result_set_ = NULL;\n", result_ref.ptr, cursor_name);
bprintf(cg_declarations_output, "%s %s_row_num_ = 0;\n", rt->cql_int32, cursor_name);
bprintf(cg_declarations_output, "%s %s_row_count_ = 0;\n", rt->cql_int32, cursor_name);
bprintf(cg_cleanup_output, " cql_object_release(%s_result_set_);\n", cursor_name);
if (cg_in_loop) {
// tricky case, the call might iterate so we have to clean up the cursor before we do the call
bprintf(cg_main_output, "cql_object_release(%s_result_set_);\n", cursor_name);
}
CHARBUF_CLOSE(result_ref);
}
else {
bprintf(cg_declarations_output, "sqlite3_stmt *%s_stmt = NULL;\n", cursor_name);
if (!is_boxed) {
// easy case, no boxing, just finalize on exit.
bprintf(cg_cleanup_output, " cql_finalize_stmt(&%s_stmt);\n", cursor_name);
if (cg_in_loop) {
// tricky case, the call might iterate so we have to clean up the cursor before we do the call
bprintf(cg_main_output, "cql_finalize_stmt(&%s_stmt);\n", cursor_name);
}
}
}
if (is_select_stmt(ast->right)) {
// DECLARE [name] CURSOR FOR [select_stmt]
// or
// DECLARE [name] CURSOR FOR [explain_stmt]
EXTRACT_ANY_NOTNULL(select_stmt, ast->right);
if (is_boxed) {
// The next prepare will finalize the statement, we don't want to do that
// if the cursor is being handled by boxes. The box downcount will take care of it
bprintf(cg_main_output, "%s_stmt = NULL;\n", cursor_name);
}
cg_bound_sql_statement(cursor_name, select_stmt, CG_PREPARE|CG_MINIFY_ALIASES);
}
else if (is_unboxing) {
// DECLARE [name] CURSOR FOR [named_box_object]
CHARBUF_OPEN(box_name);
bprintf(cg_main_output, "%s_stmt = cql_unbox_stmt(%s);\n", cursor_name, ast->right->sem->name);
bprintf(&box_name, "%s_object_", cursor_name);
cg_copy(cg_main_output, box_name.ptr, SEM_TYPE_OBJECT, ast->right->sem->name);
CHARBUF_CLOSE(box_name);
}
else {
// DECLARE [name] CURSOR FOR [call_stmt]]
if (is_boxed) {
// The next prepare will finalize the statement, we don't want to do that
// if the cursor is being handled by boxes. The box downcount will take care of it
bprintf(cg_main_output, "%s_stmt = NULL;\n", cursor_name);
}
EXTRACT_NOTNULL(call_stmt, ast->right);
cg_call_stmt_with_cursor(call_stmt, cursor_name);
}
if (is_boxed) {
// An object will control the lifetime of the cursor. If the cursor is boxed
// this is the object reference that will be used. This way the exit path is
// uniform regardless of whether or not the object was in fact boxed in the
// control flow. This is saying that it might be boxed later so we use this
// general mechanism for lifetime. The cg_var_decl helper handles cleanup too.
CHARBUF_OPEN(box_name);
bprintf(&box_name, "%s_object_", cursor_name);
cg_var_decl(cg_declarations_output, SEM_TYPE_OBJECT, box_name.ptr, CG_VAR_DECL_LOCAL);
// the unbox case gets the object from the unbox operation above, so skip if unboxing
if (!is_unboxing) {
// Note we have to clear the stashed box object and then accept the new box without
// increasing the retain count on the new box because it starts with a +1 as usual.
// This is a job for cg_copy_for_create!
CHARBUF_OPEN(box_value);
bprintf(&box_value, "cql_box_stmt(%s_stmt)", cursor_name);
cg_copy_for_create(cg_main_output, box_name.ptr, SEM_TYPE_OBJECT, box_value.ptr);
CHARBUF_CLOSE(box_value);
}
CHARBUF_CLOSE(box_name);
}
if (name_ast->sem->sem_type & SEM_TYPE_HAS_SHAPE_STORAGE) {
cg_declare_auto_cursor(cursor_name, name_ast->sem);
}
else {
// if it's a global cursor (`!in_proc`) we have to assume we will need the
// variable eventually so we emit it now; if it's a local then we only emit
// it if we know it'll be used later on (as indicated by `SEM_TYPE_FETCH_INTO`)
if (!in_proc || name_ast->sem->sem_type & SEM_TYPE_FETCH_INTO) {
// make the cursor_has_row hidden variable
CHARBUF_OPEN(temp);
bprintf(&temp, "_%s_has_row_", cursor_name);
cg_var_decl(cg_declarations_output, SEM_TYPE_BOOL | SEM_TYPE_NOTNULL, temp.ptr, CG_VAR_DECL_LOCAL);
CHARBUF_CLOSE(temp);
}
}
}