function check_response($dbh)
{
    $name = script_param("name");
    $place = script_param("place");
    $choices = script_param("choices");
    $response = script_param("response");
    # Is the user's response the correct birthplace?
    if ($response == $place) {
        print "That is correct!<br />\n";
        printf("%s was born in %s.<br />\n", htmlspecialchars($name), htmlspecialchars($place));
        print "Try the next question:<br /><br />\n";
        present_question($dbh);
    } else {
        printf("\"%s\" is not correct.  Please try again.<br /><br />\n", htmlspecialchars($response));
        $choices = explode("#", $choices);
        display_form($name, $place, $choices);
    }
}
function update_entry($dbh)
{
    # Get script parameters; trim whitespace from the ID, but not
    # from the password, because the password must match exactly,
    # or from the row, because it is an array.
    $member_id = trim(script_param("member_id"));
    $password = script_param("password");
    $row = script_param("row");
    $member_id = trim($member_id);
    if (empty($member_id)) {
        die("No member ID was specified\n");
    }
    if (!ctype_digit($member_id)) {
        # must look like integer
        die("Invalid member ID was specified (must be an integer)\n");
    }
    if (!check_pass($dbh, $member_id, $password) && !check_pass($dbh, 0, $password)) {
        die("Invalid password\n");
    }
    # Examine the metadata for the member table to determine whether
    # each column allows NULL values. (Make sure nullability is
    # retrieved in uppercase.)
    $stmt = "SELECT COLUMN_NAME, UPPER(IS_NULLABLE)\n           FROM INFORMATION_SCHEMA.COLUMNS\n           WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?";
    $sth = $dbh->prepare($stmt);
    $sth->execute(array("sampdb", "member"));
    $nullable = array();
    while ($info = $sth->fetch()) {
        $nullable[$info[0]] = $info[1] == "YES";
    }
    # Iterate through each field in the form, using the values to
    # construct an UPDATE statement that contains placeholders, and
    # the array of data values to bind to the placeholders.
    $stmt = "UPDATE member ";
    $delim = "SET";
    $params = array();
    foreach ($row as $col_name => $val) {
        $stmt .= "{$delim} {$col_name}=?";
        $delim = ",";
        # if a form value is empty, update the corresponding column value
        # with NULL if the column is nullable.  This prevents trying to
        # put an empty string into the expiration date column when it
        # should be NULL, for example.
        $val = trim($val);
        if (empty($val)) {
            if ($nullable[$col_name]) {
                $params[] = NULL;
            } else {
                $params[] = "";
            }
            # enter empty string
        } else {
            $params[] = $val;
        }
    }
    $stmt .= " WHERE member_id = ?";
    $params[] = $member_id;
    $sth = $dbh->prepare($stmt);
    $sth->execute($params);
    printf("<br /><a href=\"%s\">Edit another member record</a>\n", script_name());
}
function enter_scores($dbh)
{
    # Get event ID number and array of scores for the event
    $event_id = script_param("event_id");
    #@ _GET_SCORE_PARAM_
    $score = script_param("score");
    #@ _GET_SCORE_PARAM_
    if (!ctype_digit($event_id)) {
        # must look like integer
        die("Bad event ID\n");
    }
    # Prepare the statements that are executed repeatedly
    $sth_del = $dbh->prepare("DELETE FROM score\n                             WHERE event_id = ? AND student_id = ?");
    $sth_repl = $dbh->prepare("REPLACE INTO score\n                              (event_id,student_id,score)\n                              VALUES(?,?,?)");
    # enter scores within a transaction
    try {
        $dbh->beginTransaction();
        $blank_count = 0;
        $nonblank_count = 0;
        foreach ($score as $student_id => $new_score) {
            $new_score = trim($new_score);
            if (empty($new_score)) {
                # if no score is provided for student in the form, delete any
                # score the student may have had in the database previously
                ++$blank_count;
                $sth = $sth_del;
                $params = array($event_id, $student_id);
            } else {
                if (ctype_digit($new_score)) {
                    # if a score is provided, replace any score that
                    # might already be present in the database
                    ++$nonblank_count;
                    $sth = $sth_repl;
                    $params = array($event_id, $student_id, $new_score);
                } else {
                    throw new PDOException("invalid score: {$new_score}");
                }
            }
            $sth->execute($params);
        }
        # transaction succeeded, commit it
        $dbh->commit();
        printf("Number of scores entered: %d<br />\n", $nonblank_count);
        printf("Number of scores missing: %d<br />\n", $blank_count);
    } catch (PDOException $e) {
        printf("Score entry failed: %s<br />\n", htmlspecialchars($e->getMessage()));
        # roll back, but use empty exception handler to catch rollback failure
        try {
            $dbh->rollback();
        } catch (PDOException $e) {
        }
    }
    print "<br />\n";
}