static bool_t cg_call_in_cte()

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