Games-Go-AGA-TDListDB

 view release on metacpan or  search on metacpan

lib/Games/Go/AGA/TDListDB.pm  view on Meta::CPAN

    }

    return $self->{db};
}

# library of statement handles
sub sth {
    my ($self, $name, $new) = @_;

    if (not $name) {
        $self->my_print("Statement handle name is required\n");
        die;
    }
    if (@_ > 2) {
        $self->{sth}{$name} = $new;
    }

    my $sth = $self->{sth}{$name};
    if (not $sth) {
        $self->my_print("No statement handle called '$name'\n");
        die;
    }

    return $sth;
}

sub _db_schema {
    my ($self) = @_;

    $self->db->do(
        join('',
            'CREATE TABLE IF NOT EXISTS ',
            $self->table_name,
            ' (',
                $self->sql_column_types,
            ' )',
        ),
    );

    $self->db->do(join '',
        'CREATE TABLE IF NOT EXISTS ',
        $self->table_name_meta,
        ' (',
            'key         INTEGER PRIMARY KEY, ',
            'update_time VARCHAR(12), ',
            'next_tmp_id VARCHAR(12)',
        ' )',
    );

    $self->db->do(join '',
        'INSERT OR IGNORE INTO ',
        $self->table_name_meta,
        ' (',
            'key, ',
            'update_time, ',
            'next_tmp_id',
        ' ) VALUES ( 1, 0, 1 )',
    );
}

sub update_time {
    my ($self, $new) = @_;

    if (@_ > 1) {
        $self->sth('update_time')->execute($new);
    }
    $self->sth('select_time')->execute();
    my $time = $self->sth('select_time')->fetchall_arrayref();
    $time = $time->[0][0];
    return $time || 0;
}

sub select_id {
    my ($self, $id) = @_;

    $self->sth('select_id')->execute($id);
    # ID is primary index, so can only be one - fetch into first array
    # element:
    my ($player) = $self->sth('select_id')->fetchall_arrayref;
    $player->[$self->column_idx('rank')] += 0 if (is_Rating($player->[$self->column_idx('rank')]));   # numify ratings
    return wantarray
        ? @{$player->[0]}
        : $player->[0];
}

sub insert_player {
    my ($self, @new) = @_;

    $new[$self->column_idx('id')] = $self->next_tmp_id(1) if (not $new[$self->column_idx('id')]);
    $self->sth('insert_player')->execute(@new);
    return wantarray
        ? @new
        : \@new;
}

sub next_tmp_id {
    my ($self, $used) = @_;

    $self->sth('select_next_tmp')->execute;
    my $next_tmp = $self->sth('select_next_tmp')->fetchall_arrayref;
    $next_tmp = $next_tmp->[0][0];
    $next_tmp ||= 1;
    while ($self->select_id("TMP$next_tmp")) {
        $next_tmp++
    }

    if ($used) {    # is the caller planning on allocating this one?
        $self->sth('update_next_tmp')->execute($next_tmp + 1);
    }
    return "TMP$next_tmp";
}

# reap any child zombies from earlier update_from_AGA calls
sub reap {
    my $kid;
    my $reaped = 0;
    do {
        $kid = waitpid(-1, WNOHANG);
        $reaped++ if ($kid > 0);
    } while $kid > 0;
    return $reaped;
}

sub update_from_AGA {
    my ($self) = @_;

    my $pid;
    if ($self->background) {
        $pid = fork;
        die "fork failed: $!\n" if not defined $pid;
    }
    if ($pid) {
        # parent process
        return;
    }

    if (not $self->{ua}) {
        $self->{ua} = LWP::UserAgent->new;
    }

    my $fname = $self->raw_filename;
    my $url = $self->url;
    $self->my_print("Starting $fname fetch from $url at ", scalar localtime, "\n") if ($self->verbose);
    $self->{ua}->mirror($url, $fname);
    $self->my_print("... fetch done at ", scalar localtime, "\n") if ($self->verbose);
    $self->update_from_file($fname);

    exit if (defined $pid); # exit if this is a spawned child ($pid == 0)
}

sub update_from_file {
    my ($self, $fh) = @_;

    if (not ref $fh) {
        my $fname = $fh;
        $fh = undef;
        if (not open($fh, '<', $fname)) {
            $self->my_print("Error opening $fname for reading: $!\n");
            die;
        }
    }
    $self->fh($fh);

    my $parser = Games::Go::AGA::Parse::TDList->new();
    my $verbose = $self->verbose;
    $self->my_print("Starting database update at ", scalar localtime, "\n") if ($verbose);
    $self->db->do('BEGIN');
    my $error_count = 0;
    my $ii = 0;
    my $ID = $self->column_idx('id');
    while (1) {
        $ii++;
        my $line = $self->next_line;
        last if (not defined $line);
        next if (not $line);

        if ($verbose) {
            $self->my_print('.')  if ($ii % 1000 == 0);
            $self->my_print("\n") if ($ii % 40000 == 0);
        }
        try {   # in case a line crashes, print error but continue
#$self->my_print("parse $line") if ($verbose);
            $parser->parse($line);
            my $update = $parser->as_array;
            if ($update->[$self->column_idx('last_name')] or $update->[$self->column_idx('first_name')]) {
                push @{$update}, $self->extra_columns_callback->($self, $update);
                if ($update->[$ID]) {
                    if ($update->[$ID] =~ m/tmp/i) {
                        die "TMP IDs not allowed in TDList input";
                    }
                }
                else {
                    $self->sth('select_by_name')->execute($update->[$self->column_idx('last_name')], $update->[$self->column_idx('first_name')]);
                    my $players = $self->sth('select_by_name')->fetchall_arrayref;
                    for my $player (@{$players}) {
                        if ($player->[$ID] =~ m/tmp/i) {
                            $update->[$ID] = $player->[$ID];  # already in DB (hope it's the same guy!)
                        }
                    }
                    if (not $update->[$ID]) {
                        $update->[$ID] = $self->next_tmp_id(1);
                    }
                }
                if ($self->select_id($update->[$ID])) {
                    # ID is already in database, do an update
                    $self->sth('update_id')->execute(
                        @{$update},     # new values for all columns
                        $update->[$ID],  # player ID (for WHERE condition)
                    );
                }
                else {



( run in 0.976 second using v1.01-cache-2.11-cpan-524268b4103 )