<?php
/***********************************************************************
 * Utilities to make it easier to do a database update
 *
 * do_query() writes to a log file when an update is applied,
 * and refuses to apply an update listed in the log file.
 * Supports multiple database queries per update function.
 *
 * For backward compatibility you can put the names of updates you have
 * applied by hand in the log file, without the timestamp.
 *
 * Eric Myers <myers@spy-hill.net> - 5 August 2008
 * @(#) $Id: $
\***********************************************************************/

// Updates are recorded in the log file once they are successfully applied.
// We only need to locate the log file once.
//
$config_xml = get_config();
$config_vars = parse_element($config_xml,"<config>");
$log_dir = trim(parse_element($config_vars,"<log_dir>"));
#echo "Log directory: $log_dir \n";
if( !file_exists($log_dir) || !is_dir($log_dir) ){
    die("Log file directory '$log_dir' cannot be found.\n");
 }
$log_file = $log_dir ."/db_update.log";
#echo "Log file: $log_file \n";


$prev_caller="";                // name of previously attempted update
$update_number=0;               // to count multiple queries per update


// This function finds the name of the function which is applying the update.
//
function called_by($n=2){
    $x = debug_backtrace(); // requires PHP 4.3.0 or newer
    $y = $x[$n];
    return $y['function'];
}


// Set descriptive text for the update.
// This is optional, but better than comments since it goes to log.
//
function description($text){
    global $update_desc;
    $update_desc = $text;
}
function describe($text){// alternate name
    description($text);
} 


// Execute an SQL query to do the update.
// First check the log to see if we already did it.
// If not, do the update, and log it.
//
function do_query($query) {
    global $prev_caller, $update_number;
    global $update_desc;                // descriptive text for the update
    global $log_file;

    $caller = called_by();     // name of the update function which called us

    if( $caller == $prev_caller ){ // another query for same update?
        $update_number++;
    }
    else {
        $prev_caller = $caller;
        $update_number = 1;
    }
    $query_tag = "$caller($update_number)";

    // Unix name of person who applied the update, for the log
    //
    $userinfo = posix_getpwuid(posix_getuid());
    $username = $userinfo['name'];

    // Has this update already been applied?
    //
    if( file_exists($log_file) ){
        if( $fh = fopen($log_file,'r') ){
            while( $line = fgets($fh) ){
                $n = strpos($line, $query_tag);
                if( $n !== FALSE && $n < 60) {// ignores description text
                    echo "$query_tag has ALREADY been applied. \n";
                    fclose($fh);
                    return FALSE;
                }
                // Allow for just update name by itself to block update
                $n = strpos($line, $caller); 
                if( ($n !== FALSE) && ($n < 25)){// only name by itself, no timestamp 
                     if($update_number==1){// but only flag the first query
                        echo "$caller has ALREADY been applied. \n";
                     }
                    fclose($fh);
                    return FALSE;
                }
            }
        }
    }

    // Try to apply the update.  If successful, write it to the log file.
    //
    $result = mysql_query($query);
    #$result = true; // Debugging
    if( !$result ){// TODO: better test for SQL errors?
        die("$query_tag failed! \n".mysql_error());
    }
    else {
        echo "$query_tag applied. \n";
        if( !$fd=fopen($log_file, 'a') ){
            die("CANNOT WRITE $query_tag TO LOG FILE $log_file\n");
        }
        else {
            fwrite($fd, gmdate('c'). " $query_tag ");
            if( $username ) fwrite($fd, "by $username ");
            fwrite($fd,"$update_desc\n"); 
            fclose($fd);
            $update_desc=""; // clear the description text for next item
        }
    }
}

$cvs_version_tracker[]=        //Generated automatically - do not edit
    "\$Id: $";
?>
