// returns bool inserted
bool db_primitive_insert_game( bool &signal_error, const char *white, const char *black, const char *event, const char *site, const char *round, const char *result,
                                    const char *date, const char *white_elo, const char *black_elo, const char *eco,
                                    int nbr_moves, thc::Move *moves, uint64_t *hashes  )
{
    signal_error = false;       // use this mechanism because returning false doesn't necessarily mean error
    char *errmsg;
    char insert_buf[2000];
    char blob_txt_buf[4000];    // about 500 moves each
    char blob_buf[2000];
    char white_buf[200];
    char black_buf[200];
    char event_buf[200];
    char site_buf[200];
    char round_buf[200];
    char eco_buf[200];
    if( nbr_moves < 3 )    // skip 'games' with zero, one or two moves
        return false;           // not inserted, not error
    if( white_elo && black_elo )
    {
        int elo_w = atoi(white_elo);
        int elo_b = atoi(black_elo);
        if( 0<elo_w && elo_w<2000 && 0<elo_b && elo_b<2000 )
            // if any elo information, need at least one good player
            return false;   // not inserted, not error
    }
    bool alphaWhite = sanitise( white,  white_buf, sizeof(white_buf) );
    bool alphaBlack = sanitise( black,  black_buf, sizeof(black_buf) );
    if( !alphaWhite || !alphaBlack )    // skip games with no names
        return false;   // not inserted, not error
    sanitise( event,  event_buf, sizeof(event_buf) );
    sanitise( site, site_buf,  sizeof(site_buf) );
    sanitise( round, round_buf,  sizeof(round_buf) );
    sanitise( eco,  eco_buf,  sizeof(eco_buf) );
    CompressMoves press;
    uint64_t hash = press.cr.Hash64Calculate();
    uint64_t counter=0, game_hash=hash;
    char *put_txt = blob_txt_buf;
    char *put = blob_buf;
    for( int i=0; i<nbr_moves && put_txt<blob_txt_buf+sizeof(blob_txt_buf)-10; i++ )
    {
        thc::Move mv = moves[i];
        char c = press.CompressMove(mv);
        *put++ = c;
        char hi = (c>>4)&0x0f;
        *put_txt++ = (hi>=10 ? hi-10+'A' : hi+'0');
        char lo = c&0x0f;
        *put_txt++ = (lo>=10 ? lo-10+'A' : lo+'0');
        hash = press.cr.Hash64Update( hash, mv );
        game_hash = game_hash ^ hash ^ counter;
        counter++;
    }
    *put_txt = '\0';
    *put = '\0';

    // Try to insert the game
    sprintf( insert_buf, "INSERT INTO games VALUES(%d,%lld,'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s',X'%s')",
                        game_id, game_hash, white_buf, black_buf, event_buf, site_buf, round_buf, result,
                        date, white_elo, black_elo, eco_buf, blob_txt_buf );
    int retval = sqlite3_exec( handle, insert_buf,0,0,&errmsg);
    
    // This is one spot where a database failure is (kind of) expected - the game_hash has "unique" property
    if( retval && errmsg )
    {
        char buf[200];
        strncpy( buf, errmsg, sizeof(buf)-1 );
        buf[ sizeof(buf)-1 ] = '\0';
        char *p = buf;
        while( *p )
        {
            if( isalpha(*p) && isupper(*p) )
                *p = tolower(*p);
            p++;
        }
        
        // If it wasn't a "unique" issue, we bail out
        if( !strstr(buf,"unique") )
        {
            char buf2[1000];
            sprintf( buf2, "Database error: db_primitive_insert_game() 1 %s (%s)\n", errmsg, insert_buf );
            error_msg = buf2;
            signal_error = true;
            return false;   // not inserted, error
        }
        //cprintf("Non unique game hash %s (%s)\n", errmsg, insert_buf );

        // Otherwise see if the game is a duplicate of existing game (if it's not, then it will be the same
        //  game played on a different occasion)
        bool is_duplicate = db_primitive_check_for_duplicate( signal_error, game_hash, white_buf, black_buf, result, blob_buf );
        if( signal_error )
            return false;   // not inserted, error
        
        // A duplicate game is simply discarded
        if( is_duplicate )
        {
            //cprintf( "***  DUPLICATE GAME DISCARDED\n" );
            // ZOMBIE bug fix - return here, don't add bogus moves and increment game count
            //db_temporary_hack( white, black, event, site, round, result,
            //                        date, white_elo, black_elo, eco,
            //                        nbr_moves, moves  );
            return false;   // not inserted, not error
        }
        
        // If it's the same game played by different players, save it with game_hash NULL, which is doesn't have to
        //  be unique fortunately
        else
        {
            //cprintf( "***  DUPLICATE GAME: %s-%s %s %s %s\n", white_buf, black_buf, event_buf, site_buf, result, date );
            sprintf( insert_buf, "INSERT INTO games VALUES(%d,NULL,'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s',X'%s')",
                    game_id, white_buf, black_buf, event_buf, site_buf, round_buf, result,
                    date, white_elo, black_elo, eco_buf, blob_txt_buf );
            int retval = sqlite3_exec( handle, insert_buf,0,0,&errmsg);
            if( retval && errmsg )
            {
                char buf2[1000];
                sprintf( buf2, "Database error: db_primitive_insert_game() 2 %s (%s)", errmsg, insert_buf );
                signal_error = true;
                return false;   // not inserted, error
            }
            else if( retval )
            {
                char buf2[1000];
                sprintf( buf2, "Database error: db_primitive_insert_game() 2 (%s)", insert_buf );
                signal_error = true;
                return false;   // not inserted, error
            }
            sprintf( insert_buf, "INSERT INTO games_duplicates VALUES(%d,%lld,'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s',X'%s')",
                    game_id, game_hash, white_buf, black_buf, event_buf, site_buf, round_buf, result,
                    date, white_elo, black_elo, eco_buf, blob_txt_buf );
            retval = sqlite3_exec( handle, insert_buf,0,0,&errmsg);
            if( retval && errmsg )
            {
                char buf2[1000];
                sprintf( buf2, "Database error: db_primitive_insert_game() 3 %s (%s)", errmsg, insert_buf );
                signal_error = true;
                return false;   // not inserted, error
            }
            else if( retval )
            {
                char buf2[1000];
                sprintf( buf2, "Database error: db_primitive_insert_game() 3 (%s)", insert_buf );
                signal_error = true;
                return false;   // not inserted, error
            }
        }
    }
    
    // Save the position hashes
    for( int i=0; i<nbr_moves; i++ )
    {
        uint64_t hash64 = *hashes++;
        int hash32 = (int)(hash64);
        int table_nbr = ((int)(hash64>>32))&(NBR_BUCKETS-1);
        std::vector<std::pair<int,int>> *bucket = &buckets[table_nbr];
        std::pair<int,int> duo(hash32,game_id);
        bucket->push_back(duo);
        int count = bucket->size();
        if( count >= PURGE_QUOTA )
        {
            bool ok = purge_bucket(table_nbr);
            if( !ok )
            {
                signal_error = true;
                return false;   // not inserted, error
            }
        }
    }
    game_id++;
    return true;    // inserted, no error
}
Пример #2
0
void db_primitive_insert_game_multi( const char *white, const char *black, const char *event, const char *site, const char *result, int nbr_moves, thc::Move *moves, uint64_t *hashes  )
{
    //printf( "db_primitive_gameover(%s,%s)\n", white, black );
    //uint32_t *move_ptr = (uint32_t *)moves;
    char *errmsg;
    char insert_buf[2000];
    char blob_buf[1000];    // about 500 moves each
    char white_buf[200];
    char black_buf[200];
    strcpy( white_buf, white );
    char *s=white_buf;
    while( *s )
    {
        if( !isascii(*s) )
            *s = '_';
        else if( *s!=' ' && *s!='.' && *s!=',' && !isalnum(*s) )
            *s = '_';
        s++;
    }
    strcpy( black_buf, black );
    s=black_buf;
    while( *s )
    {
        if( !isascii(*s) )
            *s = '_';
        else if( *s!=' ' && *s!='.' && *s!=',' && !isalnum(*s) )
            *s = '_';
        s++;
    }
    CompressMoves press;
    char *put = blob_buf;
    for( int i=0; i<nbr_moves && put<blob_buf+sizeof(blob_buf)-10; i++ )
    {
        char buf[2];
        thc::Move mv = moves[i];
        int nbr = press.compress_move( mv, buf );
        /* std::string s = mv.TerseOut();
         if( nbr == 1 )
         printf( "compress temp> %s -> %02x\n", s.c_str(), buf[0]&0xff );
         else if( nbr == 2 )
         printf( "compress temp> %s -> %02x,%02x\n", s.c_str(), buf[0]&0xff, buf[1]&0xff );
         else
         printf( "nbr == 0 ?\n"); */
        if( nbr == 0 )
            break;
        for( int j=0; j<nbr && j<2; j++ )
        {
            char c = buf[j];
            char hi = (c>>4)&0x0f;
            *put++ = (hi>=10 ? hi-10+'A' : hi+'0');
            char lo = c&0x0f;
            *put++ = (lo>=10 ? lo-10+'A' : lo+'0');
        }
        //printf( "%s %s\n", (i<nbr_moves?"true":"false"), (put<blob_buf+sizeof(blob_buf-10) ? "true" ? "false") );
    }
    *put = '\0';
    //printf( "%d %s\n", nbr_moves, blob_buf );
    sprintf( insert_buf, "INSERT INTO games VALUES(%d,'%s','%s','%s',X'%s')", game_id, white_buf, black_buf, result, blob_buf );
    //printf( "%s\n", insert_buf );
    int retval = sqlite3_exec( handle, insert_buf,0,0,&errmsg);
    if( retval )
    {
        printf("sqlite3_exec(INSERT 1) FAILED %s\n", errmsg );
    }
    for( int i=0; i<nbr_moves; i++ )
    {
        uint64_t hash64 = *hashes++;
        int hash32 = (int)(hash64);
        int table_nbr = ((int)(hash64>>32))&(NBR_BUCKETS-1);
        std::vector<std::pair<int,int>> *bucket = &buckets[table_nbr];
        std::pair<int,int> duo(hash32,game_id);
        bucket->push_back(duo);
        int count = bucket->size();
        if( count >= PURGE_QUOTA )
            purge_bucket(table_nbr);
    }
    game_id++;
}