in sources/cg_c.c [4552:4718]
static bool_t cg_call_in_cte(ast_node *cte_body, void *context, charbuf *buffer) {
EXTRACT_NOTNULL(call_stmt, cte_body->left);
EXTRACT(cte_binding_list, cte_body->right);
EXTRACT_STRING(name, call_stmt->left);
EXTRACT_ANY(expr_list, call_stmt->right);
ast_node *ast = find_proc(name);
Contract(is_ast_create_proc_stmt(ast));
EXTRACT_NOTNULL(proc_params_stmts, ast->right);
EXTRACT(params, proc_params_stmts->left);
EXTRACT(stmt_list, proc_params_stmts->right);
EXTRACT_MISC_ATTRS(ast, misc_attrs);
bool_t saved_in_inline_function_fragment = in_inline_function_fragment;
symtab *saved_proc_arg_aliases = proc_arg_aliases;
symtab *saved_proc_cte_aliases = proc_cte_aliases;
in_inline_function_fragment = false;
symtab *new_arg_aliases = symtab_new();
proc_cte_aliases = symtab_new();
while (cte_binding_list) {
EXTRACT_NOTNULL(cte_binding, cte_binding_list->left);
EXTRACT_STRING(formal, cte_binding->right);
EXTRACT_STRING(actual, cte_binding->left);
// The "actual" might itself be an alias from the outer scope
// be sure to push that down if that's the case. One level
// is always enough because each level does its own push if
// needed.
bool_t handled = false;
if (saved_proc_cte_aliases) {
symtab_entry *entry = symtab_find(saved_proc_cte_aliases, actual);
if (entry) {
symtab_add(proc_cte_aliases, formal, entry->val);
handled = true;
}
}
if (!handled) {
// normal case, the first time a name is aliased
symtab_add(proc_cte_aliases, formal, cte_binding);
}
cte_binding_list = cte_binding_list->right;
}
if (params) {
// move to the next index if we need to alias anything
proc_cte_index++;
}
while (params) {
Invariant(is_ast_params(params));
Invariant(expr_list); // expressions match the args
EXTRACT_NOTNULL(param, params->left);
EXTRACT_ANY_NOTNULL(expr, expr_list->left);
EXTRACT_NOTNULL(param_detail, param->right);
EXTRACT_ANY_NOTNULL(param_name_ast, param_detail->left)
EXTRACT_STRING(param_name, param_name_ast);
sem_t sem_type_var = param_name_ast->sem->sem_type;
CSTR alias_name = dup_printf("_p%d_%s_", proc_cte_index, param_name);
AST_REWRITE_INFO_SET(param->lineno, param->filename);
ast_node *alias = new_ast_str(alias_name);
symtab_add(new_arg_aliases, param_name, alias);
alias->sem = new_sem(sem_type_var);
alias->sem->name = alias_name;
alias->sem->kind = param_name_ast->sem->kind;
AST_REWRITE_INFO_RESET();
// emit the declaration
cg_var_decl(cg_declarations_output, sem_type_var, alias_name, CG_VAR_DECL_LOCAL);
sem_t sem_type_expr = expr->sem->sem_type;
// evaluate the expression and assign
// note that any arg aliases here are in the context of the caller not the callee
// we're setting up the aliases for the callee right now and they aren't ready yet even
// but that's ok because the expressions are in the context of the caller.
// todo: if the evaluation has a nested select statement then we will have to re-enter
// all of this. We can either ban that (which isn't insane really) or else we can
// save the codegen state like callbacks and such so that it can re-enter. That's
// the desired path.
CG_PUSH_EVAL(expr, C_EXPR_PRI_ASSIGN);
cg_store(cg_main_output, alias_name, sem_type_var, sem_type_expr, expr_is_null.ptr, expr_value.ptr);
CG_POP_EVAL(expr);
// guaranteed to stay in lock step
params = params->right;
expr_list = expr_list->right;
}
// exactly one statment
Invariant(!stmt_list->right);
EXTRACT_ANY_NOTNULL(stmt, stmt_list->left);
// now replace the aliases for just this one bit
proc_arg_aliases = new_arg_aliases;
cg_emit_one_frag(buffer);
// we need the column names for our select
// we'll accomplish this by generating a CTE wrapper
// the column names are were already in the original text but
// we want to minify those out, we could turn off alias minification here
// but if we did that then we couldn't share the text of the fragment
// so instead we make a wrapper that has exatly the column names we need
bool_t is_nested_select = is_ast_table_or_subquery(cte_body->parent);
CHARBUF_OPEN(wrapper);
if (is_nested_select) {
// this ensures that all the columns of the select are correctly named
bprintf(&wrapper, "WITH _ns_(");
sem_struct *sptr = cte_body->sem->sptr;
for (int32_t i = 0; i < sptr->count; i++) {
bprintf(&wrapper, "%s%s", i == 0 ? "": ", ", sptr->names[i]);
}
bprintf(&wrapper, ") AS (");
cg_emit_one_frag(&wrapper);
}
if (is_ast_if_stmt(stmt)) {
EXTRACT_NOTNULL(cond_action, stmt->left);
EXTRACT_NOTNULL(if_alt, stmt->right);
EXTRACT(elseif, if_alt->left);
EXTRACT_NAMED_NOTNULL(elsenode, else, if_alt->right);
cg_fragment_cond_action(cond_action, buffer);
cg_fragment_elseif_list(elseif, elsenode, buffer);
}
else {
cg_fragment_stmt(stmt, buffer);
}
if (is_nested_select) {
bprintf(&wrapper, ") SELECT * FROM _ns_");
cg_emit_one_frag(&wrapper);
}
CHARBUF_CLOSE(wrapper);
symtab_delete(proc_arg_aliases);
symtab_delete(proc_cte_aliases);
proc_arg_aliases = saved_proc_arg_aliases;
proc_cte_aliases = saved_proc_cte_aliases;
in_inline_function_fragment = saved_in_inline_function_fragment;
return true;
}