<?PHP
 
/**
 
* @name datafromdump.php
 
* Grabbing partial SQL INSERT lines from (Mysql) DUMP file
 
* and executing OR saving them into destination file
 
 @Author Alexander Selifonov <[email protected]>
 
 @link http://www.selifan.ru
 
 @last_modified : 13.09.2010
 
*/
 
class CDataFromDump {
 
    private $tablelist = array();
 
    private $stoppedOffset = 0;
 
    private $inserted = array();
 
    private $srcfilename = '';
 
    private $outfile = '';
 
    private $_usemetadata = true;
 
    private $_metainfo = array();
 
    private $_verbose = false;
 
    private $eol = '<br />';
 
    private $_createSql = true; # include "CREATE TABLE" operators
 
    private $in_han = 0;
 
    private $out_han = 0;
 
 
    function __construct($verbose=false, $usemeta=true) {
 
        $this->_verbose = !empty($verbose);
 
        $this->_usemetadata = !empty($usemeta);
 
        if(!isset($_SERVER['REMOTE_ADDR'])) $this->eol = "\r\n"; # run from console, so EOL must be non-HTML CRLF
 
    }
 
 
    /**
 
    * Parses SQL dump file and saves or executes "INSERT INTO ..." for desired tables
 
    *
 
    * @param mixed $srcfilename
 
    * @param mixed $offset
 
    * @param mixed $destfilename
 
    */
 
    public function ParseDump($srcfilename, $tablelist='', $destfilename='',$createsql=true, $offset=0) {
 
        global $as_dbengine;
 
        set_time_limit(0); # process can take long time.
 
        $this->_createSql = !empty($createsql);
 
        if($destfilename == $srcfilename) {
 
            echo "ERROR: Never set output filename identical to the source DUMP file !".$this->eol;
 
            return false; # no table names to extract from dump
 
        }
 
        if(is_array($tablelist)) $this->tablelist = $tablelist;
 
        elseif(is_string($tablelist)) $this->tablelist = explode(',',$tablelist);
 
        if(!sizeof($this->tablelist)) {
 
            echo "ERROR: No table names passed".$this->eol;
 
            return false; # no table names to extract from dump
 
        }
 
 
        $metaw = 0;
 
        $this->srcfilename = $srcfilename;
 
        $this->in_han = @fopen($srcfilename,'r');
 
        if(!is_resource($this->in_han)) {
 
            echo "ERROR: could not open source dump $srcfilename".$this->eol;
 
            return false;
 
        }
 
        $this->out_han = false;
 
        $this->inserted = array();
 
        $this->outfile = $destfilename;
 
        if($destfilename) {
 
            $this->out_han = fopen($destfilename,'w');
 
            if(!$this->out_han) {
 
                echo "ERROR: Creating output file error, $destfilename".$this->eol;
 
                @fclose($this->in_han);
 
                return false;
 
            }
 
        }
 
 
        $metafilename = $srcfilename.'.metadata'; # generate file with meta-data
 
        if($this->_usemetadata) {
 
            $bymeta = (file_exists($metafilename) and filemtime($metafilename) == filemtime($this->srcfilename));
 
            if(!$bymeta) $metaw = @fopen($metafilename,'w'); # create (and re-fill) metadata file
 
        }
 
        if($bymeta) $this->ScanWithMeta($metafilename);
 
        else { #<3>
 
            if($offset>0) fseek($this->in_han,$offset);
 
            $lineno = 0;
 
            $curtable = '';
 
            while(!feof($this->in_han)) { #<4>
 
                $curpos = ftell($this->in_han);
 
                $line = trim(fgets($this->in_han));
 
                $lineno++;
 
                if($line==='') continue;
 
                $lbegin = strtoupper(substr($line,0,12));
 
                if($lbegin !=='INSERT INTO ' AND $lbegin!=='CREATE TABLE' ) continue;
 
                $arr = explode(' ',$line);
 
                $tname = isset($arr[2]) ? $arr[2] : '';
 
                if($curtable != $tname) {
 
                    $curtable = $tname;
 
                    # Save found beginning of INSERT INTO ... in metadata file, for future use
 
                    if(is_resource($metaw)) {
 
                        fwrite($metaw, "$curtable|$curpos\n");
 
                        if($this->_verbose) echo "Offset for $curtable is $curpos".$this->eol;
 
                    }
 
                }
 
                if(!in_array($curtable, $this->tablelist)) continue; # no meta-data mode
 
                if(substr($line,-1)!=';') { #<5>
 
                    # read multi-line INSERT operator until ';' char found
 
                    while(!feof($this->in_han)) {
 
                        $line2 = rtrim(fgets($this->in_han));
 
                        $line .= "\n$line2";
 
                        if(substr($line,-1)==';') break;
 
                    }
 
                } #<5>
 
                if($this->_createSql OR $lbegin =='INSERT INTO ') $this->processOneSql($curtable, $line);
 
                $this->stoppedOffset = ftell($this->in_han);
 
            } #<4>
 
        } #<3>
 
        fclose($this->in_han);
 
        if($this->out_han) fclose($this->out_han);
 
        # and close/touch generated meta-data file
 
        if(is_resource($metaw)) {
 
            fclose($metaw);
 
            touch($metafilename, filemtime($this->srcfilename));
 
            if($this->_verbose) echo 'Metainfo file created : '.$metafilename.$this->eol;
 
        }
 
        return true;
 
    }
 
    private function processOneSql($tablename,$sqlbody) {
 
        global $as_dbengine;
 
        if(!isset($this->inserted[$tablename])) $this->inserted[$tablename]=0;
 
        if($this->out_han) {
 
            fwrite($this->out_han, $sqlbody."\n");
 
            $this->inserted[$tablename] += 1;
 
        }
 
        else { # run SQL operator
 
            if(isset($as_dbengine) && is_object($as_dbengine)) {
 
                $as_dbengine->sql_query($sqlbody);
 
                $this->inserted[$tablename] += $as_dbengine->affected_rows();
 
            }
 
            else {
 
                mysql_query($sqlbody);
 
                $this->inserted[$tablename] += mysql_affected_rows();
 
            }
 
        }
 
    }
 
    /**
 
    * Load metadata from metadata file, created earlier
 
    *
 
    * @param mixed $fname metadata filename
 
    */
 
    private function ScanWithMeta($fname) {
 
        $itable = 0;
 
        $lns = @file($fname);
 
        $this->_metainfo = array();
 
        if(!is_array($lns)) return;
 
        foreach($lns as $ln) {
 
            $ln = explode('|', trim($ln));
 
            if(count($ln)<2) continue;
 
            if(isset($ln[1])) $this->_metainfo[$ln[0]] = $ln[1];
 
        }
 
        if(count($this->_metainfo)<1) {
 
            echo "ERROR: empty or wrong Metadata file $fname".$this->eol;
 
            return false;
 
        }
 
        if($this->_verbose) echo "Metainfo loaded for ".count($this->_metainfo)." tables".$this->eol; # debug
 
        $toffset = 0;
 
        for($itable=0; $itable<count($this->tablelist); $itable++) { #<3>
 
          if(isset($this->_metainfo[$this->tablelist[$itable]])) { #<4>
 
              $curtable = $this->tablelist[$itable];
 
              $toffset = $this->_metainfo[$this->tablelist[$itable]];
 
              @fseek($this->in_han, $toffset);
 
              $curtable = $this->tablelist[$itable];
 
              if($this->_verbose) echo "Found offset $toffset for ".$this->tablelist[$itable].$this->eol;
 
 
              while(!feof($this->in_han)) { #<5>
 
                $curpos = ftell($this->in_han);
 
                $line = trim(fgets($this->in_han));
 
                if($line==='') continue;
 
                $lbegin = strtoupper(substr($line,0,12));
 
                $skipit = true;
 
                if($lbegin =='INSERT INTO ') $skipit = false;
 
                elseif($this->_createSql && $lbegin=='CREATE TABLE') $skipit = false;
 
                else continue;
 
 
                $arr = explode(' ',$line);
 
                $tname = isset($arr[2]) ? $arr[2] : '';
 
                if($curtable != $tname) break; # get next table to process
 
                if(substr($line,-1)!=';') {
 
                    # read multi-line INSERT operator until ';' char found
 
                    while(!feof($this->in_han)) {
 
                        $line2 = rtrim(fgets($this->in_han));
 
                        $line .= "\n$line2";
 
                        if(substr($line,-1)==';') break;
 
                    }
 
                }
 
                if($this->_createSql OR $lbegin =='INSERT INTO ') $this->processOneSql($curtable, $line);
 
                $this->stoppedOffset = ftell($this->in_han);
 
              } #<5>
 
          } #<4>
 
        } #<3>
 
    }
 
 
    /**
 
    * Printing statistics about performed job (HTML format)
 
    */
 
    public function PrintStatistics() {
 
        echo "Passed table names :"; foreach($this->tablelist as $tname) echo " $tname";
 
        echo $this->eol."Parsed/Inserted data statistics<table border='1'><tr><td>table</td><td>SQL operator count</td></tr>\n";
 
        foreach($this->inserted as $tname => $added) {
 
            echo "<tr><td>$tname</td><td style=\"text-align:right\">$added</td></tr>\n";
 
        }
 
        echo "<tr><td>Source file <b>{$this->srcfilename}</b> size:</td><td style=\"text-align:right\">".number_format(filesize($this->srcfilename))."</td></tr>\n";
 
        if($this->outfile) {
 
            $outsize = @filesize($this->outfile);
 
            echo "<tr><td>Generated file <b>{$this->outfile}</b> size :</td><td style=\"text-align:right\">".number_format($outsize)."</td></tr>\n";
 
        }
 
        echo '</table>';
 
    }
 
}
 
 
 |