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