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 completescid_gamefrom 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);