static void cg_proc_result_set()

in sources/cg_c.c [7497:8043]


static void cg_proc_result_set(ast_node *ast) {
  Contract(is_ast_create_proc_stmt(ast));
  Contract(is_struct(ast->sem->sem_type));
  EXTRACT_NOTNULL(proc_params_stmts, ast->right);
  EXTRACT(params, proc_params_stmts->left);
  EXTRACT_STRING(name, ast->left);
  EXTRACT_MISC_ATTRS(ast, misc_attrs);

  bool_t suppress_result_set = misc_attrs && exists_attribute_str(misc_attrs, "suppress_result_set");
  bool_t is_private = misc_attrs && exists_attribute_str(misc_attrs, "private");

  bool_t uses_out_union = has_out_union_stmt_result(ast);

  if (!uses_out_union && (suppress_result_set || is_private)) {
    return;
  }

  bool_t uses_out = has_out_stmt_result(ast);
  bool_t result_set_proc = has_result_set(ast);

  // exactly one of these
  Invariant(uses_out + uses_out_union + result_set_proc == 1);

  bool_t dml_proc = is_dml_proc(ast->sem->sem_type);

  // sets base_fragment_name as well for the current fragment
  uint32_t frag_type = find_fragment_attr_type(misc_attrs, &base_fragment_name);

  Invariant(frag_type != FRAG_TYPE_EXTENSION);

  // register the proc name if there is a callback, the particular result type will do whatever it wants
  rt->register_proc_name && rt->register_proc_name(name);

  if (frag_type == FRAG_TYPE_BASE) {
    // When generating code for the base fragment we're only going to produce headers
    // and those headers will be for what the eventual assembly fragment name will be
    // that is the proc that will actually have the fetcher and so forth.  Note the name
    // must match this is verifeid in semantic analysis.
    name = base_fragment_name;

    // note we did this AFTER registering the true name of this proc; this avoids
    // confusing proc name listeners with potentially two copies of the same name
  }

  charbuf *h = cg_header_output;
  charbuf *d = cg_declarations_output;

  CSTR result_set_name = name;

  CHARBUF_OPEN(data_types);
  CHARBUF_OPEN(result_set_create);
  CHARBUF_OPEN(temp);
  CG_CHARBUF_OPEN_SYM(getter_prefix, name);
  CG_CHARBUF_OPEN_SYM(stored_proc_name_sym, name, "_stored_procedure_name");
  CG_CHARBUF_OPEN_SYM(result_set_sym, result_set_name, "_result_set");
  CG_CHARBUF_OPEN_SYM(result_set_ref, result_set_name, "_result_set_ref");
  CG_CHARBUF_OPEN_SYM(proc_sym, name);
  CG_CHARBUF_OPEN_SYM(row_sym, name, "_row");
  CG_CHARBUF_OPEN_SYM(data_types_sym, name, "_data_types");
  CG_CHARBUF_OPEN_SYM(data_types_count_sym, name, "_data_types_count");
  CG_CHARBUF_OPEN_SYM(col_offsets_sym, name, "_col_offsets");
  CG_CHARBUF_OPEN_SYM(refs_offset_sym, name, "_refs_offset");
  CG_CHARBUF_OPEN_SYM(identity_columns_sym, name, "_identity_columns");
  CG_CHARBUF_OPEN_SYM(result_count_sym, name, "_result_count");
  CG_CHARBUF_OPEN_SYM(fetch_results_sym, name, "_fetch_results");
  CG_CHARBUF_OPEN_SYM(copy_sym, name, "_copy");
  CG_CHARBUF_OPEN_SYM(perf_index, name, "_perf_index");
  CG_CHARBUF_OPEN_SYM(set_encoding_sym, name, "_set_encoding");

  sem_struct *sptr = ast->sem->sptr;
  uint32_t count = sptr->count;

  // setting up perf index unless we are currently emitting an extension or base fragment
  // which do not fetch result independently (the assembly query generates the fetcher)
  if (frag_type != FRAG_TYPE_BASE) {
    bprintf(h, "#define CRC_%s %lldL\n", proc_sym.ptr, (llint_t)crc_charbuf(&proc_sym));

    bprintf(d, "static int32_t %s;\n", perf_index.ptr);

    bprintf(h,
            "\n%s%s _Nonnull %s;\n",
            rt->symbol_visibility,
            rt->cql_string_ref,
            stored_proc_name_sym.ptr);
    bprintf(d, "\n%s(%s, \"%s\");\n", rt->cql_string_proc_name, stored_proc_name_sym.ptr, name);

    if (result_set_proc) {
      // First build the struct we need
      // As we walk the fields, construct the teardown operation needed
      // to clean up that field and save it.
      bprintf(d, "\ntypedef struct %s {\n", row_sym.ptr);
      cg_fields_in_canonical_order(d, sptr);
      bprintf(d, "} %s;\n", row_sym.ptr);
    }

    bprintf(h, "\n#define %s %d\n", data_types_count_sym.ptr, count);
    bprintf(&data_types,
            "\nuint8_t %s[%s] = {\n",
            data_types_sym.ptr,
            data_types_count_sym.ptr);
  }

  // If we are generating the typed getters, setup the function tables.
  // Again, extension fragments and base fragments do not define the actual tables
  // for the result that's done by the assembly fragment.
  if (options.generate_type_getters && frag_type != FRAG_TYPE_BASE) {
    bprintf(h,
      "\n%suint8_t %s[%s];\n",
      rt->symbol_visibility,
      data_types_sym.ptr,
      data_types_count_sym.ptr);
  }

  bprintf(h, "\n");

  // the base type emits this info, it's shared by all
  // so extension fragments always have this arleady as do assembly fragments
  if (frag_type == FRAG_TYPE_BASE || frag_type == FRAG_TYPE_NONE) {
     cg_result_set_type_decl(h, result_set_sym.ptr, result_set_ref.ptr);
  }

  // we may not want the getters, at all.
  bool_t suppress_getters = false;

  if (misc_attrs) {
    suppress_getters =
      exists_attribute_str(misc_attrs, "suppress_getters") ||
      exists_attribute_str(misc_attrs, "private") ||            // private implies suppress result set
      exists_attribute_str(misc_attrs, "suppress_result_set");  // and suppress result set implies suppress getters
  }

  // the index of the encode context column, -1 represents not found
  int16_t encode_context_index = -1;
  // we may want the setters.
  bool_t emit_setters = misc_attrs && exists_attribute_str(misc_attrs, "emit_setters");

  // For each field emit the _get_field method
  for (int32_t i = 0; i < count; i++) {
    sem_t sem_type = sptr->semtypes[i];
    CSTR col = sptr->names[i];

    // Neither base fragments nor extension fragments declare the result data shape
    // the assembly fragement does that, all columns will be known at that time.
    if (frag_type != FRAG_TYPE_BASE) {
      bprintf(&data_types, "  ");
      bool_t encode = should_encode_col(col, sem_type, use_encode, encode_columns);
      cg_data_type(&data_types, encode, sem_type);
      bprintf(&data_types, ", // %s\n", col);

      if (encode_context_column != NULL && !strcmp(col, encode_context_column)) {
        encode_context_index = (int16_t)i;
      }
    }

    if (suppress_getters) {
      continue;
    }

    sem_t core_type = core_type_of(sem_type);
    bool_t col_is_nullable = is_nullable(sem_type);

    function_info info = {
      .name = name,
      .col = col,
      .col_index = i,
      .headers = h,
      .defs = d,
      .uses_out = uses_out,
      .result_set_ref_type = result_set_ref.ptr,
      .row_struct_type = row_sym.ptr,
      .frag_type = frag_type,
    };

    // if the current row is equal or greater than the base query count
    // is considered a private accesor since it belongs to the extension accessors
    if (frag_type == FRAG_TYPE_ASSEMBLY) {
      // we already know the base compiled with no errors
      ast_node *base_proc = find_base_fragment(base_fragment_name);
      Invariant(base_proc);
      Invariant(base_proc->sem);
      Invariant(base_proc->sem->sptr);

      uint32_t col_count_for_base = base_proc->sem->sptr->count;
      Invariant(col_count_for_base > 0);
    }

    if (options.generate_type_getters) {
      if (col_is_nullable && !is_ref_type(sem_type)) {
        info.ret_type = SEM_TYPE_BOOL | SEM_TYPE_NOTNULL;
        info.name_type = SEM_TYPE_NULL;
        info.sym_suffix = "_is_null";
        cg_proc_result_set_type_based_getter(&info);

        info.ret_type = core_type | SEM_TYPE_NOTNULL;
        info.name_type = core_type;
        info.sym_suffix = "_value";
        cg_proc_result_set_type_based_getter(&info);
      }
      else {
        info.ret_type = sem_type;
        info.name_type = core_type;
        info.sym_suffix = NULL;
        cg_proc_result_set_type_based_getter(&info);
        if (emit_setters) {
          cg_proc_result_set_setter(&info, DO_USE_INLINE, DONT_EMIT_SET_NULL);
        }
      }
    }
    else if (col_is_nullable && is_numeric(sem_type)) {
      info.ret_type = SEM_TYPE_BOOL | SEM_TYPE_NOTNULL;
      info.sym_suffix = "_is_null";
      info.value_suffix = ".is_null";
      cg_proc_result_set_getter(&info);

      info.ret_type = core_type | SEM_TYPE_NOTNULL,
      info.sym_suffix = "_value";
      info.value_suffix = ".value";
      cg_proc_result_set_getter(&info);

      if (emit_setters) {
        info.name_type = core_type;
        cg_proc_result_set_setter(&info, DONT_USE_INLINE, DONT_EMIT_SET_NULL);

        // set null setter
        info.sym_suffix = "_to_null";
        cg_proc_result_set_setter(&info, DONT_USE_INLINE, DO_EMIT_SET_NULL);
      }
    }
    else {
      info.ret_type = sem_type;
      info.sym_suffix = NULL;
      info.value_suffix = NULL;
      cg_proc_result_set_getter(&info);

      if (emit_setters) {
        info.name_type = core_type;
        cg_proc_result_set_setter(&info, DONT_USE_INLINE, DONT_EMIT_SET_NULL);
      }
    }

    if (use_encode && sensitive_flag(sem_type)) {
      CG_CHARBUF_OPEN_SYM_WITH_PREFIX(
        col_getter_sym,
        rt->symbol_prefix,
        info.name,
        "_get_",
        info.col,
        "_is_encoded");

      bprintf(
          info.headers,
          "\n#define %s(rs) \\\n"
          "  %s((%s)rs, %d)\n",
          col_getter_sym.ptr,
          rt->cql_result_set_get_is_encoded,
          rt->cql_result_set_ref,
          i);
      CHARBUF_CLOSE(col_getter_sym);
    }
  }

  if (options.generate_type_getters) {
    bprintf(h, "\n");
  }

  CHARBUF_OPEN(is_null_getter);

  bool_t generate_copy_attr = misc_attrs && exists_attribute_str(misc_attrs, "generate_copy");

  // Check whether we need to generate a copy function.
  bool_t generate_copy = (generate_copy_attr ||
                         (rt->proc_should_generate_copy && rt->proc_should_generate_copy(name)));

  int32_t refs_count = refs_count_sptr(sptr);

  // Skip generating reference and column offsets for extension and base fragments since they always
  // delegate to assembly fragment for retrieving results with proper index
  if (frag_type != FRAG_TYPE_BASE) {
    bprintf(&data_types, "};\n");
    bprintf(d, data_types.ptr);

    if (refs_count && !uses_out) {
      // note: fetch procs have already emitted this.
      cg_refs_offset(d, sptr, refs_offset_sym.ptr, row_sym.ptr);
    }

    cg_col_offsets(d, sptr, col_offsets_sym.ptr, row_sym.ptr);
  }

  bool_t has_identity_columns = cg_identity_columns(h, d, name, misc_attrs, identity_columns_sym.ptr);

  bprintf(&result_set_create,
          "(%s)%s(%s, count, %d, %s, meta)",
          result_set_ref.ptr,
          rt->cql_result_set_ref_new,
          uses_out ? "row" : "b.ptr",
          count,
          data_types_sym.ptr);

  CHARBUF_CLOSE(is_null_getter);

  // Emit foo_result_count, which is really just a proxy to cql_result_set_get_count,
  // but it is hiding the cql_result_set implementation detail from the API of the generated
  // code by providing a proc-scoped function for it with the typedef for the result set.

  bclear(&temp);
  bprintf(&temp, "%s %s(%s _Nonnull result_set)", rt->cql_int32, result_count_sym.ptr, result_set_ref.ptr);
  bprintf(h, "%s%s;\n", rt->symbol_visibility, temp.ptr);

  // the base fragment doesn't emit the row count symbol, this is done by the assembly; the base
  // fragment only emits the header for it.  In fact the base fragment only emits headers in general.

  if (frag_type != FRAG_TYPE_BASE) {
    bprintf(d, "\n%s {\n", temp.ptr);
    bprintf(d, "  return %s((cql_result_set_ref)result_set);\n", rt->cql_result_set_get_count);
    bprintf(d, "}\n");
  }

  // Skip generating fetch result function for base fragments since they always get
  // results fetched through the assembly query
  if (frag_type != FRAG_TYPE_BASE) {
    if (uses_out) {
      // Emit foo_fetch_results, it has the same signature as foo only with a result set
      // instead of a statement.
      bclear(&temp);
      cg_emit_fetch_results_prototype(dml_proc, params, name, result_set_name, &temp);

      // ready for prototype and function begin now
      bprintf(h, "%s%s);\n", rt->symbol_visibility, temp.ptr);
      bprintf(d, "\n%s) {\n", temp.ptr);

      // emit profiling start signal
      bprintf(d, "  cql_profile_start(CRC_%s, &%s);\n", proc_sym.ptr, perf_index.ptr);

      // one row result set from out parameter

      bprintf(d, "  *result_set = NULL;\n");
      bprintf(d, "  %s *row = (%s *)calloc(1, sizeof(%s));\n", row_sym.ptr, row_sym.ptr, row_sym.ptr);
      bprintf(d, "  ");

      // optional db arg and return code
      if (dml_proc) {
        bprintf(d, "cql_code rc = %s(_db_, ", proc_sym.ptr);
      }
      else {
        bprintf(d, "%s(", proc_sym.ptr);
      }

      if (params) {
        cg_param_names(params, d);
        bprintf(d, ", ");
      }
      bprintf(d, "row);\n");

      fetch_result_info info = {
          .dml_proc = dml_proc,
          .use_stmt = 0,
          .data_types_sym = data_types_sym.ptr,
          .col_offsets_sym = col_offsets_sym.ptr,
          .refs_count = refs_count,
          .refs_offset_sym = refs_offset_sym.ptr,
          .has_identity_columns = has_identity_columns,
          .identity_columns_sym = identity_columns_sym.ptr,
          .row_sym = row_sym.ptr,
          .proc_sym = proc_sym.ptr,
          .perf_index = perf_index.ptr,
          .misc_attrs = misc_attrs,
          .indent = 2,
          .encode_context_index = encode_context_index,
      };

      cg_fetch_info(&info, d);

      if (dml_proc) {
        bprintf(d, "  return ");
      }
      else {
        bprintf(d, "  ");
      }
      bprintf(d, "cql_one_row_result(&info, (char *)row, row->_has_row_, (cql_result_set_ref *)result_set);\n");
      bprintf(d, "}\n\n");
    }
    else if (result_set_proc) {
      // Emit foo_fetch_results, it has the same signature as foo only with a result set
      // instead of a statement.
      bclear(&temp);
      cg_emit_fetch_results_prototype(EMIT_DML_PROC, params, name, result_set_name, &temp);

      // To create the rowset we make a byte buffer object.  That object lets us
      // append row data to an in-memory stream.  Each row is fetched by binding
      // to a row object.  We use cg_get_column to read the columns.  The row
      // object of course has exactly the right type for each column.
      bprintf(h, "%s%s);\n", rt->symbol_visibility, temp.ptr);
      bprintf(d, "\n%s) {\n", temp.ptr);
      bprintf(d, "  sqlite3_stmt *stmt = NULL;\n");

      // emit profiling start signal
      bprintf(d, "  cql_profile_start(CRC_%s, &%s);\n", proc_sym.ptr, perf_index.ptr);

      // Invoke the base proc to get the statement
      bprintf(d, "  cql_code rc = %s(_db_, &stmt", proc_sym.ptr);
      if (params) {
        bprintf(d, ", ");
        cg_param_names(params, d);
      }
      bprintf(d, ");\n");

      // Now read in in all the rows using this fetch information
      fetch_result_info info = {
          .dml_proc = 1,
          .use_stmt = 1,
          .data_types_sym = data_types_sym.ptr,
          .col_offsets_sym = col_offsets_sym.ptr,
          .refs_count = refs_count,
          .refs_offset_sym = refs_offset_sym.ptr,
          .has_identity_columns = has_identity_columns,
          .identity_columns_sym = identity_columns_sym.ptr,
          .row_sym = row_sym.ptr,
          .proc_sym = proc_sym.ptr,
          .perf_index = perf_index.ptr,
          .misc_attrs = misc_attrs,
          .indent = 2,
          .encode_context_index = encode_context_index,
      };

      cg_fetch_info(&info, d);
      bprintf(d, "  return cql_fetch_all_results(&info, (cql_result_set_ref *)result_set);\n");
      bprintf(d, "}\n\n");
    }
    else {
      // this is the only case left
      Invariant(uses_out_union);

      fetch_result_info info = {
          .dml_proc = 0,
          .use_stmt = 0,
          .data_types_sym = data_types_sym.ptr,
          .col_offsets_sym = col_offsets_sym.ptr,
          .refs_count = refs_count,
          .refs_offset_sym = refs_offset_sym.ptr,
          .has_identity_columns = has_identity_columns,
          .identity_columns_sym = identity_columns_sym.ptr,
          .row_sym = row_sym.ptr,
          .proc_sym = proc_sym.ptr,
          .perf_index = perf_index.ptr,
          .misc_attrs = misc_attrs,
          .indent = 0,
          .prefix = proc_sym.ptr,
          .encode_context_index = encode_context_index,
      };

      cg_fetch_info(&info, d);
    }
  }

  if (generate_copy) {
    bprintf(h,
            "#define %s(result_set, result_set_to%s) \\\n"
            "%s((cql_result_set_ref)(result_set))->copy( \\\n"
            "  (cql_result_set_ref)(result_set), \\\n"
            "  (cql_result_set_ref *)(result_set_to), \\\n"
            "  %s, \\\n"
            "  %s)\n",
            copy_sym.ptr,
            uses_out ? "": ", from, count",
            rt->cql_result_set_get_meta,
            uses_out ? "0" : "from",
            uses_out ? "1" : "count");
  }

  if (rt->generate_equality_macros) {
    bclear(&temp);

    CG_CHARBUF_OPEN_SYM(hash_sym, name, uses_out ? "_hash" : "_row_hash");
    bprintf(h,
            "#define %s(result_set%s) "
            "%s((cql_result_set_ref)(result_set))->rowHash((cql_result_set_ref)(result_set), %s)\n",
            hash_sym.ptr,
            uses_out ? "" : ", row",
            rt->cql_result_set_get_meta,
            uses_out ? "0" : "row");
    CHARBUF_CLOSE(hash_sym);

    CG_CHARBUF_OPEN_SYM(equal_sym, name, uses_out ? "_equal" : "_row_equal");
    bprintf(h,
            "#define %s(rs1%s, rs2%s) \\\n"
            "%s((cql_result_set_ref)(rs1))->rowsEqual( \\\n"
            "  (cql_result_set_ref)(rs1), \\\n"
            "  %s, \\\n"
            "  (cql_result_set_ref)(rs2), \\\n"
            "  %s)\n",
            equal_sym.ptr,
            uses_out ? "" : ", row1",
            uses_out ? "" : ", row2",
            rt->cql_result_set_get_meta,
            uses_out ? "0" : "row1",
            uses_out ? "0" : "row2");
    CHARBUF_CLOSE(equal_sym);

    if (has_identity_columns) {
      CG_CHARBUF_OPEN_SYM(same_sym, name, uses_out ? "_same" : "_row_same");
      bprintf(h, "#define %s(rs1%s, rs2%s) \\\n"
              "%s((cql_result_set_ref)(rs1))->rowsSame( \\\n"
              "  (cql_result_set_ref)(rs1), \\\n"
              "  %s, \\\n"
              "  (cql_result_set_ref)(rs2), \\\n"
              "  %s)\n",
              same_sym.ptr,
              uses_out ? "" : ", row1",
              uses_out ? "" : ", row2",
              rt->cql_result_set_get_meta,
              uses_out ? "0" : "row1",
              uses_out ? "0" : "row2");
      CHARBUF_CLOSE(same_sym);
    }
  }

  // Add a helper function that overrides CQL_DATA_TYPE_ENCODED bit of a resultset.
  // It's a debugging function that allow you to turn ON/OFF encoding/decoding when
  // your app is running.
  if (use_encode) {
    bprintf(h, "\nextern void %s(%s col, %s encode);\n", set_encoding_sym.ptr, rt->cql_int32, rt->cql_bool);
    bprintf(d, "void %s(%s col, %s encode) {\n", set_encoding_sym.ptr, rt->cql_int32, rt->cql_bool);
    bprintf(d, "  return cql_set_encoding(%s, %s, col, encode);\n", data_types_sym.ptr, data_types_count_sym.ptr);
    bprintf(d, "}\n\n");
  }

  CHARBUF_CLOSE(set_encoding_sym);
  CHARBUF_CLOSE(perf_index);
  CHARBUF_CLOSE(copy_sym);
  CHARBUF_CLOSE(fetch_results_sym);
  CHARBUF_CLOSE(result_count_sym);
  CHARBUF_CLOSE(identity_columns_sym);
  CHARBUF_CLOSE(refs_offset_sym);
  CHARBUF_CLOSE(col_offsets_sym);
  CHARBUF_CLOSE(data_types_count_sym);
  CHARBUF_CLOSE(data_types_sym);
  CHARBUF_CLOSE(row_sym);
  CHARBUF_CLOSE(proc_sym);
  CHARBUF_CLOSE(result_set_ref);
  CHARBUF_CLOSE(result_set_sym);
  CHARBUF_CLOSE(stored_proc_name_sym);
  CHARBUF_CLOSE(getter_prefix);
  CHARBUF_CLOSE(temp);
  CHARBUF_CLOSE(result_set_create);
  CHARBUF_CLOSE(data_types);
}