Skip to content

How to Work with In-Memory Databases

This guide shows how to instantiate and operate transient in-memory databases with the libscid C ABI: storing games, retrieving records by 1-based index, generating filtered subsets, and applying multi-criteria sorting.


1. Overview of Symbols

  • scid_database_create_memory: Allocates a new transient in-memory database.
  • scid_database_is_open: Checks whether the database handle is active and open.
  • scid_database_game_add: Appends a game entity into the database.
  • scid_database_game_count_get: Returns the total number of games stored in the database.
  • scid_database_game_get: Materialises and loads a complete scid_game from a 1-based game index.
  • scid_database_filter_create: Creates a filtered view across the database.
  • scid_database_sort: Sorts database records according to specified criteria.
  • scid_database_free: Closes and releases the database and its allocated resources.

2. Complete Recipe

#include <scid/scid.h>

#include <stdio.h>
#include <string.h>

static int
check(
    scid_error  error,
    const char* call)
{
    if (error == SCID_OK)
    {
        return 1;
    }

    fprintf(stderr, "%s failed with scid_error %hu\n", call, error);
    return 0;
}


static int
text_equals(
    const char* text,
    size_t      text_size,
    const char* expected)
{
    return text_size == strlen(expected) && strncmp(text, expected, text_size) == 0;
}


int
main(void)
{
    const char*    start_fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
    const char*    pgn = "[Event \"Memory\"]\n"
                         "[White \"Alpha\"]\n"
                         "[Black \"Beta\"]\n"
                         "[Result \"1-0\"]\n"
                         "\n"
                         "1. e4 e5 2. Nf3 1-0\n";
    scid_database* database = NULL;
    scid_game*     game = NULL;
    scid_game*     loaded = NULL;
    scid_position* position = NULL;
    scid_filter_id filter_id = 0;
    scid_search_header_criteria* header_criteria = NULL;
    char                         diagnostic[1024];
    char                         flags[22];
    char                         text[128];
    size_t                       diagnostic_size = 0;
    size_t                       count = 0;
    size_t                       game_indexes[2] = {0};
    size_t                       flags_size = 0;
    size_t                       listed_count = 0;
    size_t                       sorted_position = 0;
    size_t                       text_size = 0;
    int                          is_open = 0;

    if (!check(scid_database_create_memory("example", &database), "scid_database_create_memory") ||
        !check(scid_database_is_open(database, &is_open), "scid_database_is_open") || !is_open ||
        !check(scid_database_game_count_get(database, &count), "scid_database_game_count_get") ||
        count != 0 ||
        !check(
            scid_position_create_from_fen(start_fen, &position), "scid_position_create_from_fen") ||
        !check(
            scid_game_create(
                position, pgn, strlen(pgn), &game, diagnostic, sizeof(diagnostic),
                &diagnostic_size),
            "scid_game_create"))
    {
        fprintf(stderr, "%.*s\n", (int)diagnostic_size, diagnostic);
        scid_position_free(position);
        scid_game_free(game);
        scid_database_free(database);
        return 1;
    }

    printf("open: %d\n", is_open);
    printf("games before add: %zu\n", count);

    if (!check(scid_database_game_add(database, game, NULL), "scid_database_game_add") ||
        !check(scid_database_game_count_get(database, &count), "scid_database_game_count_get") ||
        count != 1)
    {
        scid_position_free(position);
        scid_game_free(game);
        scid_database_free(database);
        return 1;
    }

    printf("games after first add: %zu\n", count);

    if (!check(scid_database_game_add(database, game, "M"), "scid_database_game_add") ||
        !check(scid_database_game_count_get(database, &count), "scid_database_game_count_get") ||
        count != 2)
    {
        scid_position_free(position);
        scid_game_free(game);
        scid_database_free(database);
        return 1;
    }

    printf("games after second add: %zu\n", count);

    if (!check(
            scid_search_header_criteria_create(&header_criteria),
            "scid_search_header_criteria_create") ||
        !check(
            scid_search_header_criteria_white_set(header_criteria, "Alpha"),
            "scid_search_header_criteria_white_set") ||
        !check(scid_database_filter_create(database, &filter_id), "scid_database_filter_create") ||
        !check(
            scid_database_search_headers(
                database, SCID_FILTER_ALL_GAMES, filter_id, header_criteria, NULL, NULL, NULL,
                NULL),
            "scid_database_search_headers") ||
        !check(
            scid_database_filter_game_count_get(database, filter_id, &count),
            "scid_database_filter_game_count_get") ||
        count != 2 ||
        !check(
            scid_database_filter_game_indices_get(
                database, filter_id, "d+", 0, 2, game_indexes, 2, &listed_count),
            "scid_database_filter_game_indices_get") ||
        listed_count != 2 || game_indexes[0] != 0 || game_indexes[1] != 1 ||
        !check(
            scid_database_filter_game_row_for_index_get(
                database, filter_id, "d+", game_indexes[1], &sorted_position),
            "scid_database_filter_game_row_for_index_get") ||
        sorted_position != 1 ||
        !check(
            scid_database_game_get(
                database, game_indexes[1], &loaded, flags, sizeof(flags), &flags_size),
            "scid_database_game_get") ||
        !text_equals(flags, flags_size, "M") ||
        !check(
            scid_game_tag_get(loaded, "Event", text, sizeof(text), &text_size),
            "scid_game_tag_get") ||
        !text_equals(text, text_size, "Memory") ||
        !check(
            scid_game_mainline_halfmove_count_get(loaded, &count),
            "scid_game_mainline_halfmove_count_get") ||
        count != 3)
    {
        scid_search_header_criteria_free(header_criteria);
        scid_database_filter_delete(database, filter_id);
        scid_game_free(loaded);
        scid_position_free(position);
        scid_game_free(game);
        scid_database_free(database);
        return 1;
    }

    printf("loaded flags: %.*s\n", (int)flags_size, flags);
    printf("loaded event: %.*s\n", (int)text_size, text);
    printf("loaded halfmoves: %zu\n", count);

    scid_search_header_criteria_free(header_criteria);
    scid_database_filter_delete(database, filter_id);
    scid_game_free(loaded);
    scid_position_free(position);
    scid_game_free(game);
    scid_database_free(database);
    return 0;
}

3. Key Concepts and Patterns

Database Indices are 1-Based

Following Scid's heritage, database game indices in libscid are 1-based (i.e. games range from index 1 to game_count inclusive):

scid_database* database = NULL;
scid_database_create_memory("example", &database);

/* Add game */
scid_database_game_add(database, game);

/* Retrieve game at index 1 */
scid_game* loaded_game = NULL;
scid_database_game_get(database, 1, &loaded_game);

/* Cleanup */
scid_game_free(loaded_game);
scid_database_free(database);