static void cg_schema_manage_recreate_tables()

in sources/cg_schema.c [1047:1221]


static void cg_schema_manage_recreate_tables(
  charbuf *output,
  charbuf *decls,
  recreate_annotation *notes,
  size_t count)
{
  Contract(notes);
  Contract(count);

  CHARBUF_OPEN(recreate);
  CHARBUF_OPEN(update_tables);
  CHARBUF_OPEN(pending_table_creates);

  // non-null-callbacks will generate SQL for Sqlite (no attributes)
  gen_sql_callbacks callbacks;
  init_gen_sql_callbacks(&callbacks);
  callbacks.mode = gen_mode_no_annotations;

  crc_t table_crc = 0;

  for (size_t i = 0; i < count; i++) {
    recreate_annotation *note = &notes[i];

    EXTRACT_NOTNULL(recreate_attr, note->annotation_ast);
    EXTRACT(delete_attr, recreate_attr->right);

    // this coveres either deleted or unsubscribed

    ast_node *ast = note->target_ast;
    ast_node *ast_output = ast;
    bool_t deleted = is_deleted(ast);

    Invariant(is_ast_create_table_stmt(ast));

    bool_t is_eponymous = false;

    if (is_virtual_ast(ast)) {
      ast_output = ast->parent;
      Invariant(is_ast_create_virtual_table_stmt(ast_output));

      EXTRACT_NOTNULL(module_info, ast_output->left);
      EXTRACT_NOTNULL(create_table_name_flags, ast->left);
      EXTRACT_NOTNULL(table_flags_attrs, create_table_name_flags->left);
      EXTRACT_OPTION(flags, table_flags_attrs->left);
      is_eponymous = !!(flags & VTAB_IS_EPONYMOUS);
    }

    if (!include_from_region(ast->sem->region, SCHEMA_TO_UPGRADE)) {
      continue;
    }

    if (is_eponymous) {
      // eponymous virtual tables do not get created or deleted
      continue;
    }

    EXTRACT_NOTNULL(create_table_name_flags, ast->left);
    EXTRACT_NOTNULL(table_flags_attrs, create_table_name_flags->left);
    EXTRACT_STRING(table_name, create_table_name_flags->right);

    // recreate if needed

    CHARBUF_OPEN(make_table);

    if (!deleted) {
      gen_set_output_buffer(&make_table);
      gen_statement_with_callbacks(ast_output, &callbacks);
      bprintf(&make_table, ";\n");
    }

    // note that this will also drop any indices that are on the table
    bprintf(&update_tables, "    DROP TABLE IF EXISTS %s;\n", table_name);

    list_item *index_list = ast->sem->index_list;

    // now create the various indices but not the deleted ones
    for (list_item *item = index_list; item; item = item->next) {
      ast_node *index = item->ast;
      // deleted index, don't recreate it
      if (index->sem->delete_version > 0) {
        continue;
      }
      gen_statement_with_callbacks(index, &callbacks);
      bprintf(&make_table, ";\n");
    }

    table_crc ^= crc_charbuf(&make_table);

    // Now we have to remember that the tables in the recreate annotations have been
    // sorted by reverse ordinal, meaning they are in the correct order to DROP
    // we emit the DROP statements first as normal but the creates have to be
    // stashed in a pending buffer that accumulates in the reverse order.  When we're
    // done with the group, then we emit the whole batch of creates in the natural order.
    // This is done in one pass because there could be filtering and whatnot and so
    // this way we know we get exactly the right tables.  It does mean some buffer
    // shuffling.

    // This only matters for recreate groups, with none-groups this is a big no-op
    // Note also that CQL0060 prevents anyone from taking an FK on a table that is
    // recreate and either not in a group at all or in a different group.  So only
    // the tables in the processed group could have FKs and those are handled correctly here.

    CHARBUF_OPEN(temp);
    bindent(&temp, &make_table, 4);
    bprintf(&temp, "%s", pending_table_creates.ptr);
    bclear(&pending_table_creates);
    bprintf(&pending_table_creates, "%s", temp.ptr);
    CHARBUF_CLOSE(temp);

    CHARBUF_CLOSE(make_table);

    CSTR gname = note->group_name;

    // if there is a group and and this node can be merged with the next
    // then hold the update and accumulate the CRC
    if (i + 1 < count && gname[0] && !Strcasecmp(gname, (note+1)->group_name)) {
      continue;
    }

    bprintf(&update_tables, "%s", pending_table_creates.ptr);
    bclear(&pending_table_creates);

    CHARBUF_OPEN(facet);

    CSTR migrate_key = NULL;

    if (gname[0]) {
      // we're updating the whole group
      bprintf(&facet, "%s_group_crc", gname);
      migrate_key = gname;
    }
    else {
      bprintf(&facet, "%s_table_crc", table_name);
      migrate_key = table_name;
    }

    ast_node *migration = find_recreate_migrator(migrate_key);
    if (migration) {
      EXTRACT_STRING(proc, migration->right);
      CHARBUF_OPEN(migrate_table);

      bprintf(&migrate_table, "\n    -- recreate migration procedure required\n");
      bprintf(&migrate_table, "    CALL %s();\n\n", proc);

      bprintf(&update_tables, migrate_table.ptr);
      bprintf(decls, "DECLARE PROC %s() USING TRANSACTION;\n", proc);

      table_crc ^= crc_charbuf(&migrate_table);

      CHARBUF_CLOSE(migrate_table);
    }

    bprintf(&recreate, "  IF cql_facet_find(%s_facets, '%s') != %lld THEN\n", global_proc_name, facet.ptr, (llint_t)table_crc);
    bprintf(&recreate, "%s", update_tables.ptr);
    bprintf(&recreate, "    CALL %s_cql_set_facet_version('%s', %lld);\n", global_proc_name, facet.ptr, (llint_t)table_crc);
    bprintf(&recreate, "  END IF;\n");

    CHARBUF_CLOSE(facet);

    // once we emit, we reset the CRC we've been accumulating and reset the buffer of table recreates
    table_crc = 0;
    bclear(&update_tables);
  }

  bprintf(output, "-- recreate all the @recreate tables that might have changed\n");
  bprintf(output, "@attribute(cql:private)\n");
  bprintf(output, "CREATE PROCEDURE %s_cql_recreate_tables()\n", global_proc_name);
  bprintf(output, "BEGIN\n");
  bprintf(output, "%s", recreate.ptr);
  bprintf(output, "END;\n\n");

  CHARBUF_CLOSE(pending_table_creates);
  CHARBUF_CLOSE(update_tables);
  CHARBUF_CLOSE(recreate);
}