static void cg_call_stmt_with_cursor()

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);
}