Changeset 260

Show
Ignore:
Timestamp:
10/23/07 02:11:30 (9 months ago)
Author:
hans
Message:

#167 - Adding formatter support to PDOSQLExecTask

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/2.3/classes/phing/tasks/defaults.properties

    r259 r260  
    4545 
    4646creole=phing.tasks.ext.creole.CreoleSQLExecTask 
    47 pdo=phing.tasks.ext.PDOSQLExecTask 
     47pdo=phing.tasks.ext.pdo.PDOSQLExecTask 
    4848package-as-path=phing.tasks.ext.PackageAsPathTask 
    4949smarty=phing.tasks.ext.SmartyTask 
  • branches/2.3/classes/phing/tasks/ext/pdo/PDOSQLExecTask.php

    r256 r260  
    2020 */ 
    2121 
    22 require_once 'phing/tasks/ext/PDOTask.php'; 
     22require_once 'phing/tasks/ext/pdo/PDOTask.php'; 
    2323include_once 'phing/system/io/StringReader.php'; 
     24include_once 'phing/tasks/ext/pdo/PDOSQLExecFormatterElement.php'; 
    2425 
    2526/** 
     
    5455class PDOSQLExecTask extends PDOTask { 
    5556 
     57        /** 
     58         * Count of how many statements were executed successfully. 
     59         * @var int 
     60         */ 
    5661        private $goodSql = 0; 
     62 
     63        /** 
     64         * Count of total number of SQL statements. 
     65         * @var int 
     66         */ 
    5767        private $totalSql = 0; 
    5868 
     
    6272    /** 
    6373     * Database connection 
     74     * @var PDO 
    6475     */ 
    6576    private $conn = null; 
    6677 
    6778    /** 
    68      * files to load 
     79     * Files to load 
     80     * @var array FileSet[] 
    6981     */ 
    7082    private $filesets = array(); 
    7183 
    7284    /** 
     85     * Formatter elements. 
     86     * @var array PDOSQLExecFormatterElement[] 
     87     */ 
     88    private $formatters = array(); 
     89 
     90    /** 
    7391     * SQL statement 
    74      */ 
    75     private $statement = null; 
     92     * @var PDOStatement 
     93     */ 
     94    private $statement; 
    7695 
    7796    /** 
    7897     * SQL input file 
    79      */ 
    80     private $srcFile = null; 
     98     * @var PhingFile 
     99     */ 
     100    private $srcFile; 
    81101 
    82102    /** 
    83103     * SQL input command 
     104     * @var string 
    84105     */ 
    85106    private $sqlCommand = ""; 
     
    91112 
    92113    /** 
    93      * SQL Statement delimiter 
     114     * SQL Statement delimiter (for parsing files) 
     115     * @var string 
    94116     */ 
    95117    private $delimiter = ";"; 
     
    100122     */ 
    101123    private $delimiterType = "normal"; // can't use constant just defined 
    102  
    103     /** 
    104      * Print SQL results. 
    105      */ 
    106     private $print = false; 
    107  
    108     /** 
    109      * Print header columns. 
    110      */ 
    111     private $showheaders = true; 
    112  
    113     /** 
    114      * Results Output file. 
    115      */ 
    116     private $output = null; 
    117  
    118124 
    119125    /** 
     
    128134 
    129135    /** 
    130      * Append to an existing file or overwrite it? 
    131      */ 
    132     private $append = false; 
    133  
    134     /** 
    135136     * Fetch mode for PDO select queries. 
    136137     * @var int 
     
    159160    public function addFileset(FileSet $set) { 
    160161        $this->filesets[] = $set; 
     162    } 
     163 
     164    /** 
     165     * Creates a new PDOSQLExecFormatterElement for <formatter> element. 
     166     * @return PDOSQLExecFormatterElement 
     167     */ 
     168    public function createFormatter() 
     169    { 
     170        $fe = new PDOSQLExecFormatterElement($this); 
     171        $this->formatters[] = $fe; 
     172        return $fe; 
    161173    } 
    162174 
     
    204216        $this->delimiterType = $delimiterType; 
    205217    } 
    206  
    207     /** 
    208      * Set the print flag. 
    209      * 
    210      * @param boolean $print 
    211      */ 
    212     public function setPrint($print) 
    213     { 
    214         $this->print = (boolean) $print; 
    215     } 
    216  
    217     /** 
    218      * Print headers for result sets from the  
    219      * statements; optional, default true. 
    220      * @param boolean $showheaders 
    221      */ 
    222     public function setShowheaders($showheaders) { 
    223         $this->showheaders = (boolean) $showheaders; 
    224     } 
    225  
    226     /** 
    227      * Set the output file;  
    228      * optional, defaults to the console. 
    229      * @param PhingFile $output 
    230      */ 
    231     public function setOutput(PhingFile $output) { 
    232         $this->output = $output; 
    233     } 
    234  
    235     /** 
    236      * whether output should be appended to or overwrite 
    237      * an existing file.  Defaults to false. 
    238      * @param $append 
    239      */ 
    240     public function setAppend($append) { 
    241         $this->append = (boolean) $append; 
    242     } 
    243  
    244218 
    245219    /** 
     
    268242 
    269243    /** 
     244     * Gets a default output writer for this task. 
     245     * @return Writer 
     246     */ 
     247    private function getDefaultOutput() 
     248    { 
     249        return new LogWriter($this); 
     250    } 
     251 
     252    /** 
    270253     * Load the sql file and then execute it 
    271254     * @throws BuildException 
     
    277260        if ($this->fetchMode === null) { 
    278261                $this->fetchMode = PDO::FETCH_BOTH; 
     262        } 
     263 
     264        // Initialize the formatters here.  This ensures that any parameters passed to the formatter 
     265        // element get passed along to the actual formatter object 
     266        foreach($this->formatters as $fe) { 
     267                $fe->prepare(); 
    279268        } 
    280269 
     
    324313                        $this->statement = null; 
    325314 
    326                         $out = null; 
     315                        // Initialize the formatters. 
     316                        $this->initFormatters(); 
    327317 
    328318                        try { 
    329  
    330                                 if ($this->output !== null) { 
    331                                         $this->log("Opening output file " . $this->output, Project::MSG_VERBOSE); 
    332                                         $out = new BufferedWriter(new FileWriter($this->output->getAbsolutePath(), $this->append)); 
    333                                 } 
    334319 
    335320                                // Process all transactions 
     
    339324                                                $this->conn->beginTransaction(); 
    340325                                        } 
    341                                         $this->transactions[$i]->runTransaction($out); 
     326                                        $this->transactions[$i]->runTransaction(); 
    342327                                        if (!$this->isAutocommit()) { 
    343328                                                $this->log("Commiting transaction", Project::MSG_VERBOSE); 
     
    345330                                        } 
    346331                                } 
    347                                 if ($out) $out->close(); 
    348332                        } catch (Exception $e) { 
    349                                 if ($out) $out->close(); 
    350333                                throw $e; 
    351334                        } 
     
    354337                                try { 
    355338                                        $this->conn->rollback(); 
    356                                 } catch (SQLException $ex) {} 
     339                                } catch (PDOException $ex) {} 
    357340                        } 
    358341                        throw new BuildException($e->getMessage(), $this->location); 
    359                 } catch (SQLException $e){ 
     342                } catch (PDOException $e){ 
    360343                        if (!$this->isAutocommit() && $this->conn !== null && $this->onError == "abort") { 
    361344                                try { 
    362345                                        $this->conn->rollback(); 
    363                                 } catch (SQLException $ex) {} 
     346                                } catch (PDOException $ex) {} 
    364347                        } 
    365348                        throw new BuildException($e->getMessage(), $this->location); 
    366349                } 
     350                         
     351                // Close the formatters. 
     352                $this->closeFormatters(); 
    367353 
    368354                $this->log($this->goodSql . " of " . $this->totalSql . 
    369355                " SQL statements executed successfully"); 
     356 
    370357        } catch (Exception $e) { 
    371358                $this->transactions = $savedTransaction; 
     
    382369    /** 
    383370     * read in lines and execute them 
    384      * @throws SQLException, IOException  
    385      */ 
    386     public function runStatements(Reader $reader, Writer $out = null) { 
     371     * @throws PDOException, IOException  
     372     */ 
     373    public function runStatements(Reader $reader) { 
    387374        $sql = ""; 
    388375        $line = ""; 
     
    420407                        && $line == $this->delimiter) { 
    421408                                $this->log("SQL: " . $sql, Project::MSG_VERBOSE); 
    422                                 $this->execSQL(StringHelper::substring($sql, 0, strlen($sql) - strlen($this->delimiter) - 1), $out); 
     409                                $this->execSQL(StringHelper::substring($sql, 0, strlen($sql) - strlen($this->delimiter) - 1)); 
    423410                                $sql = ""; 
    424411                        } 
     
    427414                // Catch any statements not followed by ; 
    428415                if ($sql !== "") { 
    429                         $this->execSQL($sql, $out); 
    430                 } 
    431         } catch (SQLException $e) { 
     416                        $this->execSQL($sql); 
     417                } 
     418        } catch (PDOException $e) { 
    432419                throw new BuildException("Error running statements", $e); 
    433420        } 
    434421    } 
    435          
     422 
    436423    /** 
    437424     * Whether the passed-in SQL statement is a SELECT statement. 
     
    442429     * @return boolean Whether specified SQL looks like a SELECT query. 
    443430     */ 
    444        protected function isSelectSql($sql) 
    445        
    446               $sql = trim($sql); 
    447               return (stripos($sql, 'select') === 0 && stripos($sql, 'select into ') !== 0); 
    448        
    449          
     431    protected function isSelectSql($sql) 
     432   
     433      $sql = trim($sql); 
     434      return (stripos($sql, 'select') === 0 && stripos($sql, 'select into ') !== 0); 
     435   
     436 
    450437    /** 
    451438     * Exec the sql statement. 
    452      * @throws SQLException  
    453      */ 
    454     protected function execSQL($sql, Writer $out = null) { 
     439     * @throws PDOException  
     440     */ 
     441    protected function execSQL($sql) { 
     442 
    455443        // Check and ignore empty statements 
    456444        if (trim($sql) == "") { 
     
    460448        try { 
    461449                $this->totalSql++; 
    462                  
    463                 # FIXME - currently, this only works for update statements 
     450 
    464451                $this->statement = $this->conn->prepare($sql); 
    465452                $this->statement->execute(); 
    466453                $this->log($this->statement->rowCount() . " rows affected", Project::MSG_VERBOSE); 
    467                  
    468                 if ($this->isSelectSql($sql) && $this->print) { 
    469                         $this->printResults($out); 
    470                 } else { 
    471                         $this->statement->closeCursor(); 
    472                 } 
    473                  
     454 
     455                $this->processResults(); 
     456 
     457                $this->statement->closeCursor(); 
     458                $this->statement = null; 
     459 
    474460                $this->goodSql++; 
    475461 
    476         } catch (SQLException $e) { 
     462        } catch (PDOException $e) { 
    477463                $this->log("Failed to execute: " . $sql, Project::MSG_ERR); 
    478464                if ($this->onError != "continue") { 
     
    484470 
    485471    /** 
    486      * print any results in the statement. 
    487      * @throw SQLException 
    488      */ 
    489     protected function printResults(Writer $out = null) { 
    490  
    491         $this->log("Processing new result set.", Project::MSG_VERBOSE); 
    492  
    493         $line = ""; 
    494  
    495         $colsprinted = false; 
    496  
    497         while ($row = $this->statement->fetch($this->fetchMode)) { 
    498                  
    499                 if (!$colsprinted && $this->showheaders) { 
    500                         $first = true; 
    501                         foreach($row as $fieldName => $ignore) { 
    502                                 if ($first) $first = false; else $line .= ","; 
    503                                 $line .= $fieldName; 
    504                         } 
    505                         if ($out !== null) { 
    506                                 $out->write($line); 
    507                                 $out->newLine(); 
    508                         } else { 
    509                                 print($line.PHP_EOL); 
    510                         } 
    511                         $line = ""; 
    512                         $colsprinted = true; 
    513                 } // if show headers 
    514  
    515                 $first = true; 
    516                 foreach($row as $columnValue) { 
    517  
    518                         if ($columnValue != null) { 
    519                                 $columnValue = trim($columnValue); 
    520                         } 
    521  
    522                         if ($first) { 
    523                                 $first = false; 
    524                         } else { 
    525                                 $line .= ","; 
    526                         } 
    527                         $line .= $columnValue; 
    528                 } 
    529  
    530                 if ($out !== null) { 
    531                         $out->write($line); 
    532                         $out->newLine(); 
    533                 } else { 
    534                         print($line . PHP_EOL); 
    535                 } 
    536                 $line = ""; 
    537  
    538         } 
    539  
    540         // Addresses some issues w/ PDO 
    541         // See: http://verens.com/archives/2006/10/19/pdosqlite-gotcha/ 
    542         $this->statement = null; 
    543  
    544         if ($out !== null) { 
    545                 $out->newLine(); 
    546         } else { 
    547                 print(PHP_EOL); 
    548         } 
     472     * Returns configured PDOResultFormatter objects (which were created from PDOSQLExecFormatterElement objects). 
     473     * @return array PDOResultFormatter[] 
     474     */ 
     475    protected function getConfiguredFormatters() 
     476    { 
     477        $formatters = array(); 
     478        foreach ($this->formatters as $fe) { 
     479                $formatters[] = $fe->getFormatter(); 
     480        } 
     481        return $formatters; 
     482    } 
     483 
     484    /** 
     485     * Initialize the formatters. 
     486     */ 
     487    protected function initFormatters() { 
     488        $formatters = $this->getConfiguredFormatters(); 
     489        foreach ($formatters as $formatter) { 
     490                $formatter->initialize(); 
     491        } 
     492 
     493    } 
     494 
     495    /** 
     496     * Run cleanup and close formatters. 
     497     */ 
     498    protected function closeFormatters() { 
     499        $formatters = $this->getConfiguredFormatters(); 
     500        foreach ($formatters as $formatter) { 
     501                $formatter->close(); 
     502        } 
     503    } 
     504 
     505    /** 
     506     * Passes results from query to any formatters. 
     507     * @throw PDOException 
     508     */ 
     509    protected function processResults() { 
     510 
     511        try { 
     512 
     513                $this->log("Processing new result set.", Project::MSG_VERBOSE); 
     514 
     515                $formatters = $this->getConfiguredFormatters(); 
     516 
     517                while ($row = $this->statement->fetch($this->fetchMode)) { 
     518                        foreach ($formatters as $formatter) { 
     519                                $formatter->processRow($row); 
     520                        } 
     521                } 
     522 
     523        } catch (Exception $x) { 
     524                $this->log("Error processing reults: " . $x->getMessage(), Project::MSG_ERR); 
     525                foreach ($formatters as $formatter) { 
     526                        $formatter->close(); 
     527                } 
     528                throw $x; 
     529        } 
     530 
    549531    } 
    550532} 
    551  
    552533 
    553534/** 
     
    580561 
    581562    /** 
    582      * @throws IOException, SQLException 
    583      */ 
    584     public function runTransaction($out = null
     563     * @throws IOException, PDOException 
     564     */ 
     565    public function runTransaction(
    585566    { 
    586567        if (!empty($this->tSqlCommand)) { 
    587568                $this->parent->log("Executing commands", Project::MSG_INFO); 
    588                 $this->parent->runStatements(new StringReader($this->tSqlCommand), $out); 
     569                $this->parent->runStatements(new StringReader($this->tSqlCommand)); 
    589570        } 
    590571 
     
    593574                Project::MSG_INFO); 
    594575                $reader = new FileReader($this->tSrcFile); 
    595                 $this->parent->runStatements($reader, $out); 
     576                $this->parent->runStatements($reader); 
    596577                $reader->close(); 
    597578        } 
  • branches/2.3/docs/phing_guide/book/chapters/appendixes/AppendixC-OptionalTasks.html

    r240 r260  
    518518  &lt;fileset dir=&quot;sqlfiles&quot;&gt; 
    519519          &lt;include name=&quot;*.sql&quot;/&gt; 
    520   &lt;/fileset&gt; 
    521 &lt;/pdo&gt; 
     520  &lt;/fileset&gt;<br />&lt;/pdo&gt; 
    522521</pre> 
    523522 
    524523<pre>&lt;pdo url="mysql:host=localhost;dbname=test" userid="username" password="password"> 
    525524  &lt;transaction src=&quot;path/to/sqlfile.sql&quot;/&gt; 
     525  &lt;formatter type=&quot;plain&quot; outfile=&quot;path/to/output.txt&quot;/&gt; 
    526526&lt;/pdo&gt; 
    527527</pre> 
     
    579579      <td>The action to perform on error (continue, stop, or abort)</td> 
    580580      <td>abort</td> 
    581       <td>No</td> 
    582     </tr> 
    583         <tr> 
    584       <td>output</td> 
    585       <td>File</td> 
    586       <td>The file to which output should be logged.</td> 
    587       <td>none</td> 
    588       <td>No</td> 
    589     </tr> 
    590         <tr> 
    591       <td>print</td> 
    592       <td>Boolean</td> 
    593       <td>Whether to print results of query.</td> 
    594       <td>false</td> 
    595       <td>No</td> 
    596     </tr> 
    597         <tr> 
    598       <td>showheaders</td> 
    599       <td>Boolean</td> 
    600       <td>Whether to show column headers.</td> 
    601       <td>false</td> 
    602581      <td>No</td> 
    603582    </tr> 
     
    628607  <li>fileset 
    629608    <p>Files containing SQL statements.</p> 
     609  </li> 
     610  <li>formatter 
     611         <p>The results of any queries that are executed can be printed in different formats. 
     612    Output will always be sent to a file, unless you set the <em>usefile</em> attribute to <em>false</em>. 
     613    The path to the output file file can be specified by the <em>outfile</em> attribute; there is a default filename that 
     614        will be returned by the formatter if no output file is specified.</p> 
     615    <p>There are three predefined formatters - one prints the query results in XML format, 
     616    the other emits plain text. Custom formatters that 
     617    extend phing.tasks.pdo.PDOResultFormatter can be specified.</p> 
     618        <h3>Attributes</h3> 
     619        <table> 
     620          <thead> 
     621                <tr> 
     622                  <th>Name</th> 
     623                  <th>Type</th> 
     624                  <th>Description</th> 
     625                  <th>Default</th> 
     626                  <th>Required</th> 
     627                </tr> 
     628          </thead> 
     629          <tbody> 
     630                <tr> 
     631                  <td>type</td> 
     632                  <td>String</td> 
     633                  <td> Use a predefined formatter (either <em>xml</em>, <em>plain</em>, or <em>brief</em>). </td> 
     634                  <td>n/a</td> 
     635                  <td rowspan="2">One of these attributes is required. </td> 
     636                </tr> 
     637                <tr> 
     638                  <td>classname</td> 
     639                  <td>String</td> 
     640                  <td> Name of a custom formatter class (must extend phing.tasks.ext.pdo.PDOResultFormatter). </td> 
     641                  <td>n/a</td> 
     642                </tr> 
     643                <tr> 
     644                  <td>usefile</td> 
     645                  <td>Boolean</td> 
     646                  <td> Boolean that determines whether output should be sent to a file. </td> 
     647                  <td>true</td> 
     648                  <td>No</td> 
     649                </tr> 
     650                <tr> 
     651                  <td>outfile</td> 
     652                  <td>File</td> 
     653                  <td>Path to file in which to store result. </td> 
     654                  <td>Depends on formatter</td> 
     655                  <td>No</td> 
     656                </tr> 
     657                <tr> 
     658      <td>showheaders</td> 
     659      <td>Boolean</td> 
     660      <td>(only applies to plain formatter) Whether to show column headers.</td> 
     661      <td>false</td> 
     662      <td>No</td> 
     663    </tr> 
     664        <tr> 
     665      <td>coldelim</td> 
     666      <td>String</td> 
     667      <td>(only applies to plain formatter) The column delimiter.</td> 
     668      <td>,</td> 
     669      <td>No</td> 
     670    </tr> 
     671        <tr> 
     672      <td>rowdelim</td> 
     673      <td>String</td> 
     674      <td>(only applies to plain formatter) The row delimiter.</td> 
     675      <td>\n</td> 
     676      <td>No</td> 
     677    </tr> 
     678        <tr> 
     679      <td>encoding</td> 
     680      <td>String</td> 
     681      <td>(only applies to XML formatter) The xml document encoding.</td> 
     682      <td>(PHP default)</td> 
     683      <td>No</td> 
     684    </tr> 
     685        <tr> 
     686      <td>formatoutput</td> 
     687      <td>Boolean</td> 
     688      <td>(only applies to XML formatter) Whether to format XML output.</td> 
     689      <td>true</td> 
     690      <td>No</td> 
     691    </tr> 
     692          </tbody> 
     693        </table> 
     694        <h3>Examples</h3> 
     695        <pre>&lt;pdo url="pgsql:host=localhost dbname=test"> 
     696  &lt;fileset dir=&quot;sqlfiles&quot;&gt; 
     697          &lt;include name=&quot;*.sql&quot;/&gt; 
     698  &lt;/fileset&gt; 
     699 
     700  &lt;!-- xml formatter --&gt; 
     701  &lt;formatter type=&quot;xml&quot; output=&quot;output.xml&quot;/&gt; 
     702   
     703  &lt;!-- custom formatter --&gt; 
     704  &lt;formatter classname=&quot;path.to.CustomFormatterClass&quot;&gt; 
     705    &lt;param name=&quot;someClassAttrib&quot; value=&quot;some-value&quot;/&gt; 
     706  &lt;/formatter&gt; 
     707 
     708  &lt;!-- No output file + usefile=false means it goes to phing log --&gt; 
     709  &lt;formatter type=&quot;plain&quot; usefile=&quot;false&quot; /&gt;<br />&lt;/pdo&gt; 
     710</pre> 
     711         
    630712  </li> 
    631713</ul>