cql_noexport void continue_find_table_node()

in sources/cg_common.c [259:454]


cql_noexport void continue_find_table_node(table_callbacks *callbacks, ast_node *node) {
  // Check the type of node so that we can find the direct references to tables. We
  // can't know the difference between a table or view in the ast, so we will need to
  // later find the definition to see if it points to a create_table_stmt to distinguish
  // from views.

  find_ast_str_node_callback alt_callback = NULL;
  symtab *alt_visited = NULL;
  ast_node *table_or_view_name_ast = NULL;

  if (is_ast_cte_table(node)) {
    EXTRACT_ANY_NOTNULL(cte_body, node->right);

    // this is a proxy node, it doesn't contribute anything
    // any nested select does not run.
    if (is_ast_like(cte_body)) {
      return;
    }
  }
  else if (is_ast_shared_cte(node)) {
    // if we're on a shared CTE usage, then we recurse into the CALL and
    // we recurse into the binding list.  The CALL should not be handled
    // like a normal procedure call, the body is inlined.  Note that the
    // existence of the fragment is meant to be transparent to anyone
    // downstream -- this isn't a normal call that might be invisible to us
    // we *must* have the fragment because we're talking about a semantically
    // valid shared cte binding.

    EXTRACT_NOTNULL(call_stmt, node->left);
    EXTRACT(cte_binding_list, node->right);

    EXTRACT_ANY_NOTNULL(name_ast, call_stmt->left);
    EXTRACT_STRING(name, name_ast);
    ast_node *proc = find_proc(name);
    if (proc) {
      // Look through the proc definition for tables. Just call through recursively.
      continue_find_table_node(callbacks, proc);
    }

    if (cte_binding_list) {
      continue_find_table_node(callbacks, cte_binding_list);
    }

    // no further recursion is needed
    return;
  }
  else if (is_ast_declare_cursor_like_select(node)) {
    // There is a select in this declaration but it doesn't really run, it's just type info
    // so that doesn't count.  So we don't recurse here.
    return;
  }
  else if (is_ast_cte_binding(node)) {
    EXTRACT_ANY_NOTNULL(actual, node->left);

    // handle this just like a normal table usage in a select statement (because it is)
    table_or_view_name_ast = actual;
    alt_callback = callbacks->callback_from;
    alt_visited = callbacks->visited_from;
  }
  else if (is_ast_table_or_subquery(node)) {
    EXTRACT_ANY_NOTNULL(factor, node->left);
    if (is_ast_str(factor)) {
      // the other table factor cases (there are several) do not have a string payload
      table_or_view_name_ast = factor;
      alt_callback = callbacks->callback_from;
      alt_visited = callbacks->visited_from;
    }
  }
  else if (is_ast_fk_target(node)) {
    // if we're walking a table then we'll also walk its FK's
    // normally we don't start by walking tables anyway so this doesn't
    // run if you do a standard walk of a procedure
    if (callbacks->notify_fk) {
      EXTRACT_ANY_NOTNULL(name_ast, node->left);
      table_or_view_name_ast = name_ast;
    }
  }
  else if (is_ast_drop_view_stmt(node) || is_ast_drop_table_stmt(node)) {
    if (callbacks->notify_table_or_view_drops) {
      EXTRACT_ANY_NOTNULL(name_ast, node->right);
      table_or_view_name_ast = name_ast;
    }
  }
  else if (is_ast_trigger_target_action(node)) {
    if (callbacks->notify_triggers) {
      EXTRACT_ANY_NOTNULL(name_ast, node->left);
      table_or_view_name_ast = name_ast;
    }
  }
  else if (is_ast_delete_stmt(node)) {
    EXTRACT_ANY_NOTNULL(name_ast, node->left);
    table_or_view_name_ast = name_ast;
    alt_callback = callbacks->callback_deletes;
    alt_visited = callbacks->visited_delete;
  }
  else if (is_ast_insert_stmt(node)) {
    EXTRACT(name_columns_values, node->right);
    EXTRACT_ANY_NOTNULL(name_ast, name_columns_values->left);
    table_or_view_name_ast = name_ast;
    alt_callback = callbacks->callback_inserts;
    alt_visited = callbacks->visited_insert;
  }
  else if (is_ast_update_stmt(node)) {
    EXTRACT_ANY(name_ast, node->left);
    // name_ast node is NULL if update statement is part of an upsert statement
    if (name_ast) {
      table_or_view_name_ast = name_ast;
      alt_callback = callbacks->callback_updates;
      alt_visited = callbacks->visited_update;
    }
  }
  else if (is_ast_call_stmt(node) | is_ast_call(node)) {
    // Both cases have the name in the node left so we can consolidate
    // the check to see if it's a proc is redundant in the call_stmt case
    // but it lets us share code so we just go with it.  The other case
    // is a possible proc_as_func call so we must check if the target is a proc.

    EXTRACT_ANY_NOTNULL(name_ast, node->left);
    EXTRACT_STRING(name, name_ast);
    ast_node *proc = find_proc(name);

    if (proc) {
      // this only happens for ast_call but this check is safe for both
      if (name_ast->sem && (name_ast->sem->sem_type & SEM_TYPE_INLINE_CALL)) {
        // Look through the proc definition for tables because the target will be inlined
        continue_find_table_node(callbacks, proc);
      }

      EXTRACT_STRING(canon_name, get_proc_name(proc));
      if (callbacks->callback_proc) {
        if (symtab_add(callbacks->visited_proc, canon_name, name_ast)) {
          callbacks->callback_proc(canon_name, name_ast, callbacks->callback_context);
        }
      }
    }
  }

  if (table_or_view_name_ast) {
    // Find the definition and see if we have a create_table_stmt.
    EXTRACT_STRING(table_or_view_name, table_or_view_name_ast);
    ast_node *table_or_view = find_table_or_view_even_deleted(table_or_view_name);

    // It's not actually possible to use a deleted table or view in a procedure.
    // If the name lookup here says that we found something deleted it means
    // that we have actually found a CTE that is an alias for a deleted table
    // or view. In that case, we don't want to add the thing we found to the dependency
    // set we are creating.  We don't want to make this CTE an error because
    // its reasonable to replace a deleted table/view with CTE of the same name.
    // Hence we simply filter out deleted tables/views here.
    if (table_or_view && table_or_view->sem->delete_version > 0) {
      table_or_view = NULL;
    }

    // Make sure we don't process a table or view that we've already processed.
    if (table_or_view) {
      if (is_ast_create_table_stmt(table_or_view)) {
        EXTRACT_NOTNULL(create_table_name_flags, table_or_view->left);
        EXTRACT_STRING(canonical_name, create_table_name_flags->right);

        // Found a table, execute the callback.
        if (symtab_add(callbacks->visited_any_table, canonical_name, table_or_view)) {
          callbacks->callback_any_table(canonical_name, table_or_view, callbacks->callback_context);
        }

        // Emit the second callback if any.
        if (alt_callback && symtab_add(alt_visited, canonical_name, table_or_view)) {
          alt_callback(canonical_name, table_or_view, callbacks->callback_context);
        }
      } else {
        Contract(is_ast_create_view_stmt(table_or_view));
        EXTRACT_NOTNULL(view_and_attrs, table_or_view->right);
        EXTRACT_NOTNULL(name_and_select, view_and_attrs->left);
        EXTRACT_STRING(canonical_name, name_and_select->left);

        if (symtab_add(callbacks->visited_any_table, canonical_name, table_or_view)) {
          // Report the view itself
          if (callbacks->callback_any_view) {
            callbacks->callback_any_view(canonical_name, table_or_view, callbacks->callback_context);
          }

          // Look through the view definition for tables. Just call through recursively.
          continue_find_table_node(callbacks, table_or_view);
        }
      }
    }
  }

  // Check the left and right nodes.
  if (ast_has_left(node)) {
    continue_find_table_node(callbacks, node->left);
  }

  if (ast_has_right(node)) {
    continue_find_table_node(callbacks, node->right);
  }
}