static void cg_schema_manage_indices()

in sources/cg_schema.c [903:1045]


static void cg_schema_manage_indices(charbuf *output, int32_t *drops, int32_t *creates) {
  Contract(creates);
  Contract(drops);
  CHARBUF_OPEN(create);
  CHARBUF_OPEN(drop);
  CHARBUF_OPEN(names);

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

  *drops = *creates = 0;

  for (list_item *item = all_indices_list; item; item = item->next) {
    ast_node *ast = item->ast;
    Invariant(is_ast_create_index_stmt(ast));

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

    Contract(is_ast_create_index_stmt(ast));
    EXTRACT_NOTNULL(create_index_on_list, ast->left);
    EXTRACT_NOTNULL(flags_names_attrs, ast->right);
    EXTRACT_NOTNULL(connector, flags_names_attrs->right);
    EXTRACT_NOTNULL(index_names_and_attrs, connector->left);
    EXTRACT_NOTNULL(indexed_columns, index_names_and_attrs->left);
    EXTRACT(opt_where, index_names_and_attrs->right);
    EXTRACT_ANY_NOTNULL(index_name_ast, create_index_on_list->left);
    EXTRACT_STRING(index_name, index_name_ast);
    EXTRACT_ANY_NOTNULL(table_name_ast, create_index_on_list->right);
    EXTRACT_STRING(table_name, table_name_ast);

    if (names.used > 1) {
      bprintf(&names, ",\n      '%s'", index_name);
    }
    else {
      bprintf(&names, "\n      '%s'", index_name);
    }

    if (ast->sem->delete_version > 0) {
      // delete only, we're done here
      bprintf(&drop, "  DROP INDEX IF EXISTS %s;\n", index_name);
      (*drops)++;
      continue;
    }

    // If this index is attached to a table marked @recreate then we recreate the index with the table
    // as a unit so there is nothing to do here.  The index will be in the same @recreate group as
    // the table if it has one.
    ast_node *table_ast = find_table_or_view_even_deleted(table_name);

    Invariant(table_ast);
    Invariant(table_ast->sem);
    if (table_ast->sem->recreate) {
      // recreate table ... skip it as above.
      continue;
    }

    // drop then recreate after other migrate steps

    CHARBUF_OPEN(make_index);

    gen_set_output_buffer(&make_index);
    gen_statement_with_callbacks(ast, &callbacks);
    bprintf(&make_index, ";\n");

    llint_t index_crc = (llint_t)crc_charbuf(&make_index);

    bprintf(&drop, "  IF cql_facet_find(%s_facets, '%s_index_crc') != %lld THEN\n", global_proc_name, index_name, index_crc);
    bprintf(&drop, "    DROP INDEX IF EXISTS %s;\n", index_name);
    bprintf(&drop, "  END IF;\n");

    bprintf(&create, "  IF cql_facet_find(%s_facets, '%s_index_crc') != %lld THEN\n", global_proc_name, index_name, index_crc);
    bindent(&create, &make_index, 4);
    bprintf(&create, "    CALL %s_cql_set_facet_version('%s_index_crc', %lld);\n", global_proc_name, index_name, index_crc);
    bprintf(&create, "  END IF;\n");

    CHARBUF_CLOSE(make_index);

    // we always have a mutation plan for potentially changed indices so
    // that means there is a drop and a create
    (*creates)++;
    (*drops)++;
  }

  if (options.schema_exclusive) {
    bprintf(output, "\n-- get all the unknown index names, store them in a result set\n");
    bprintf(output, "@attribute(cql:private)\n");
    bprintf(output, "CREATE PROCEDURE %s_cql_get_unknown_indices()\n", global_proc_name);
    bprintf(output, "BEGIN\n");
    bprintf(output, "  DECLARE C CURSOR FOR SELECT name from sqlite_master where type = 'index'\n");
    bprintf(output, "    AND name NOT LIKE 'sqlite%%'", names.ptr);
    if (names.used > 1) {
      bprintf(output, "\n    AND name NOT IN (%s)", names.ptr);
    }
    bprintf(output, ";\n");
    bprintf(output, "  LOOP FETCH C\n");
    bprintf(output, "  BEGIN\n");
    bprintf(output, "    OUT UNION C;\n");
    bprintf(output, "  END;\n");
    bprintf(output, "END;\n\n");

    bprintf(output, "-- drop all the indices using the fetched names\n");
    bprintf(output, "@attribute(cql:private)\n");
    bprintf(output, "CREATE PROCEDURE %s_cql_drop_unknown_indices()\n", global_proc_name);
    bprintf(output, "BEGIN\n");
    bprintf(output, "  DECLARE C CURSOR FOR CALL %s_cql_get_unknown_indices();\n", global_proc_name);
    bprintf(output, "  LOOP FETCH C\n");
    bprintf(output, "  BEGIN\n");
    bprintf(output, "    CALL cql_exec_internal(printf('DROP INDEX %%s;', C.name));\n");
    bprintf(output, "  END;\n");
    bprintf(output, "END;\n\n");

    bprintf(&drop, "  CALL %s_cql_drop_unknown_indices();\n", global_proc_name);

    // we always behave as though we have some drops in exclusive mode
    *drops = 1;
  }

  if (*drops) {
    bprintf(output, "-- drop all the indices that are deleted or changing\n");
    bprintf(output, "@attribute(cql:private)\n");
    bprintf(output, "CREATE PROCEDURE %s_cql_drop_all_indices()\n", global_proc_name);
    bprintf(output, "BEGIN\n");
    bprintf(output, "%s", drop.ptr);
    bprintf(output, "END;\n\n");
  }

  if (*creates) {
    bprintf(output, "-- create all the indices we need\n");
    bprintf(output, "@attribute(cql:private)\n");
    bprintf(output, "CREATE PROCEDURE %s_cql_create_all_indices()\n", global_proc_name);
    bprintf(output, "BEGIN\n");
    bprintf(output, "%s", create.ptr);
    bprintf(output, "END;\n\n");
  }

  CHARBUF_CLOSE(names);
  CHARBUF_CLOSE(drop);
  CHARBUF_CLOSE(create);
}