home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / phing / tasks / ext / creole / CreoleSQLExecTask.php next >
Encoding:
PHP Script  |  2007-10-25  |  18.2 KB  |  591 lines

  1. <?php
  2. /*
  3.  *  $Id: CreoleSQLExecTask.php 266 2007-10-25 01:32:38Z hans $
  4.  *
  5.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8.  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9.  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11.  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15.  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16.  *
  17.  * This software consists of voluntary contributions made by many individuals
  18.  * and is licensed under the LGPL. For more information please see
  19.  * <http://phing.info>.
  20.  */
  21.  
  22. require_once 'phing/tasks/ext/creole/CreoleTask.php';
  23. include_once 'phing/system/io/StringReader.php';
  24.  
  25. /**
  26.  * Executes a series of SQL statements on a database using Creole.
  27.  *
  28.  * <p>Statements can
  29.  * either be read in from a text file using the <i>src</i> attribute or from 
  30.  * between the enclosing SQL tags.</p>
  31.  * 
  32.  * <p>Multiple statements can be provided, separated by semicolons (or the 
  33.  * defined <i>delimiter</i>). Individual lines within the statements can be 
  34.  * commented using either --, // or REM at the start of the line.</p>
  35.  * 
  36.  * <p>The <i>autocommit</i> attribute specifies whether auto-commit should be 
  37.  * turned on or off whilst executing the statements. If auto-commit is turned 
  38.  * on each statement will be executed and committed. If it is turned off the 
  39.  * statements will all be executed as one transaction.</p>
  40.  * 
  41.  * <p>The <i>onerror</i> attribute specifies how to proceed when an error occurs 
  42.  * during the execution of one of the statements. 
  43.  * The possible values are: <b>continue</b> execution, only show the error;
  44.  * <b>stop</b> execution and commit transaction;
  45.  * and <b>abort</b> execution and transaction and fail task.</p>
  46.  *
  47.  * @author    Hans Lellelid <hans@xmpl.org> (Phing)
  48.  * @author    Jeff Martin <jeff@custommonkey.org> (Ant)
  49.  * @author    Michael McCallum <gholam@xtra.co.nz> (Ant)
  50.  * @author    Tim Stephenson <tim.stephenson@sybase.com> (Ant)
  51.  * @package   phing.tasks.ext
  52.  * @version   $Revision: 1.21 $
  53.  */
  54. class CreoleSQLExecTask extends CreoleTask {
  55.  
  56.     private $goodSql = 0;
  57.     private $totalSql = 0;
  58.  
  59.     const DELIM_ROW = "row";
  60.     const DELIM_NORMAL = "normal";
  61.  
  62.     /**
  63.      * Database connection
  64.      */
  65.     private $conn = null;
  66.  
  67.     /**
  68.      * files to load
  69.      */
  70.     private $filesets = array();
  71.  
  72.     /**
  73.      * all filterchains objects assigned to this task
  74.      */
  75.     private $filterChains  = array();
  76.  
  77.     /**
  78.      * SQL statement
  79.      */
  80.     private $statement = null;
  81.  
  82.     /**
  83.      * SQL input file
  84.      */
  85.     private $srcFile = null;
  86.  
  87.     /**
  88.      * SQL input command
  89.      */
  90.     private $sqlCommand = "";
  91.  
  92.     /**
  93.      * SQL transactions to perform
  94.      */
  95.     private $transactions = array();
  96.  
  97.     /**
  98.      * SQL Statement delimiter
  99.      */
  100.     private $delimiter = ";";
  101.     
  102.     /**
  103.      * The delimiter type indicating whether the delimiter will
  104.      * only be recognized on a line by itself
  105.      */
  106.     private $delimiterType = "normal"; // can't use constant just defined
  107.     
  108.     /**
  109.      * Print SQL results.
  110.      */
  111.     private $print = false;
  112.  
  113.     /**
  114.      * Print header columns.
  115.      */
  116.     private $showheaders = true;
  117.  
  118.     /**
  119.      * Results Output file.
  120.      */
  121.     private $output = null;
  122.  
  123.     
  124.     /**
  125.      * Action to perform if an error is found
  126.      **/
  127.     private $onError = "abort";
  128.     
  129.     /**
  130.      * Encoding to use when reading SQL statements from a file
  131.      */
  132.     private $encoding = null;
  133.  
  134.     /**
  135.      * Append to an existing file or overwrite it?
  136.      */
  137.     private $append = false;
  138.         
  139.     /**
  140.      * Set the name of the SQL file to be run.
  141.      * Required unless statements are enclosed in the build file
  142.      */
  143.     public function setSrc(PhingFile $srcFile) {       
  144.         $this->srcFile = $srcFile;
  145.     }
  146.     
  147.     /**
  148.      * Set an inline SQL command to execute. 
  149.      * NB: Properties are not expanded in this text.
  150.      */
  151.     public function addText($sql) {
  152.         $this->sqlCommand .= $sql;
  153.     }
  154.     
  155.     /**
  156.      * Adds a set of files (nested fileset attribute).
  157.      */
  158.     public function addFileset(FileSet $set) {
  159.         $this->filesets[] = $set;
  160.     }
  161.  
  162.     /**
  163.      * Creates a filterchain
  164.      *
  165.      * @access public
  166.      * @return  object  The created filterchain object
  167.      */
  168.     function createFilterChain() {
  169.         $num = array_push($this->filterChains, new FilterChain($this->project));
  170.         return $this->filterChains[$num-1];
  171.     }
  172.  
  173.     /**
  174.      * Add a SQL transaction to execute
  175.      */
  176.     public function createTransaction() {
  177.         $t = new SQLExecTransaction($this);
  178.         $this->transactions[] = $t;
  179.         return $t;
  180.     }
  181.     
  182.     /**
  183.      * Set the file encoding to use on the SQL files read in
  184.      *
  185.      * @param encoding the encoding to use on the files
  186.      */
  187.     public function setEncoding($encoding) {
  188.         $this->encoding = $encoding;
  189.     }
  190.     
  191.     /**
  192.      * Set the statement delimiter.
  193.      *
  194.      * <p>For example, set this to "go" and delimitertype to "ROW" for
  195.      * Sybase ASE or MS SQL Server.</p>
  196.      *
  197.      * @param delimiter
  198.      */
  199.     public function setDelimiter($delimiter)
  200.     {
  201.         $this->delimiter = $delimiter;
  202.     }
  203.  
  204.     /**
  205.      * Set the Delimiter type for this sql task. The delimiter type takes two
  206.      * values - normal and row. Normal means that any occurence of the delimiter
  207.      * terminate the SQL command whereas with row, only a line containing just
  208.      * the delimiter is recognized as the end of the command.
  209.      *
  210.      * @param string $delimiterType
  211.      */
  212.     public function setDelimiterType($delimiterType)
  213.     {
  214.         $this->delimiterType = $delimiterType;
  215.     }
  216.     
  217.     /**
  218.      * Set the print flag.
  219.      *
  220.      * @param boolean $print
  221.      */
  222.     public function setPrint($print)
  223.     {
  224.         $this->print = (boolean) $print;
  225.     }
  226.         
  227.     /**
  228.      * Print headers for result sets from the 
  229.      * statements; optional, default true.
  230.      * @param boolean $showheaders
  231.      */
  232.     public function setShowheaders($showheaders) {
  233.         $this->showheaders = (boolean) $showheaders;
  234.     }
  235.  
  236.     /**
  237.      * Set the output file; 
  238.      * optional, defaults to the console.
  239.      * @param PhingFile $output
  240.      */
  241.     public function setOutput(PhingFile $output) {
  242.         $this->output = $output;
  243.     }
  244.  
  245.     /**
  246.      * whether output should be appended to or overwrite
  247.      * an existing file.  Defaults to false.
  248.      * @param $append
  249.      */
  250.     public function setAppend($append) {
  251.         $this->append = (boolean) $append;
  252.     }
  253.  
  254.     
  255.     /**
  256.      * Action to perform when statement fails: continue, stop, or abort
  257.      * optional; default "abort"
  258.      */
  259.     public function setOnerror($action) {
  260.         $this->onError = $action;
  261.     }
  262.  
  263.     /**
  264.      * Load the sql file and then execute it
  265.      * @throws BuildException
  266.      */
  267.     public function main()  {
  268.             
  269.         $savedTransaction = array();
  270.         for($i=0,$size=count($this->transactions); $i < $size; $i++) {
  271.             $savedTransaction[] = clone $this->transactions[$i];
  272.         }
  273.         
  274.         $savedSqlCommand = $this->sqlCommand;
  275.  
  276.         $this->sqlCommand = trim($this->sqlCommand);
  277.  
  278.         try {
  279.             if ($this->srcFile === null && $this->sqlCommand === "" 
  280.                 && empty($this->filesets)) { 
  281.                 if (count($this->transactions) === 0) {
  282.                     throw new BuildException("Source file or fileset, "
  283.                                              . "transactions or sql statement "
  284.                                              . "must be set!", $this->location);
  285.                 }
  286.             }
  287.         
  288.             if ($this->srcFile !== null && !$this->srcFile->exists()) {
  289.                 throw new BuildException("Source file does not exist!", $this->location);
  290.             }
  291.  
  292.             // deal with the filesets
  293.             for ($i = 0,$size=count($this->filesets); $i < $size; $i++) {
  294.                 $fs = $this->filesets[$i];
  295.                 $ds = $fs->getDirectoryScanner($this->project);
  296.                 $srcDir = $fs->getDir($this->project);
  297.                 
  298.                 $srcFiles = $ds->getIncludedFiles();
  299.                 
  300.                 // Make a transaction for each file
  301.                 for ($j=0, $size=count($srcFiles); $j < $size; $j++) {
  302.                     $t = $this->createTransaction();
  303.                     $t->setSrc(new PhingFile($srcDir, $srcFiles[$j]));
  304.                 }
  305.             }
  306.             
  307.             // Make a transaction group for the outer command
  308.             $t = $this->createTransaction();
  309.             if ($this->srcFile) $t->setSrc($this->srcFile);
  310.             $t->addText($this->sqlCommand);
  311.             $this->conn = $this->getConnection();
  312.  
  313.             try {
  314.                 
  315.                 $this->statement = $this->conn->createStatement();
  316.                 
  317.                 $out = null;
  318.                 
  319.                 try {
  320.                     
  321.                     if ($this->output !== null) {
  322.                         $this->log("Opening output file " . $this->output, Project::MSG_VERBOSE);
  323.                         $out = new BufferedWriter(new FileWriter($this->output->getAbsolutePath(), $this->append));
  324.                     }
  325.                     
  326.                     // Process all transactions
  327.                     for ($i=0,$size=count($this->transactions); $i < $size; $i++) {
  328.                         $this->transactions[$i]->runTransaction($out);
  329.                         if (!$this->isAutocommit()) {
  330.                             $this->log("Commiting transaction", Project::MSG_VERBOSE);
  331.                             $this->conn->commit();
  332.                         }
  333.                     }
  334.                     if ($out) $out->close();
  335.                 } catch (Exception $e) {
  336.                     if ($out) $out->close();
  337.                     throw $e;
  338.                 } 
  339.             } catch (IOException $e) {
  340.                 if (!$this->isAutocommit() && $this->conn !== null && $this->onError == "abort") {
  341.                     try {
  342.                         $this->conn->rollback();
  343.                     } catch (SQLException $ex) {}
  344.                 }
  345.                 throw new BuildException($e->getMessage(), $this->location);
  346.             } catch (SQLException $e){
  347.                 if (!$this->isAutocommit() && $this->conn !== null && $this->onError == "abort") {
  348.                     try {
  349.                         $this->conn->rollback();
  350.                     } catch (SQLException $ex) {}
  351.                 }
  352.                 throw new BuildException($e->getMessage(), $this->location);
  353.             }
  354.             
  355.             $this->log($this->goodSql . " of " . $this->totalSql .
  356.                 " SQL statements executed successfully");
  357.         } catch (Exception $e) {
  358.             $this->transactions = $savedTransaction;
  359.             $this->sqlCommand = $savedSqlCommand;
  360.             throw $e;
  361.         }
  362.         // finally {
  363.         $this->transactions = $savedTransaction;
  364.         $this->sqlCommand = $savedSqlCommand;
  365.         
  366.     }
  367.  
  368.  
  369.     /**
  370.      * read in lines and execute them
  371.      * @throws SQLException, IOException 
  372.      */
  373.     public function runStatements(Reader $reader, $out = null) {
  374.         $sql = "";
  375.         $line = "";
  376.  
  377.         $buffer = '';
  378.  
  379.         if ((is_array($this->filterChains)) && (!empty($this->filterChains))) {    
  380.             $in = FileUtils::getChainedReader(new BufferedReader($reader), $this->filterChains, $this->getProject());
  381.             while(-1 !== ($read = $in->read())) { // -1 indicates EOF
  382.                    $buffer .= $read;
  383.             }
  384.             $lines = explode("\n", $buffer);
  385.         } else {
  386.             $in = new BufferedReader($reader);
  387.  
  388.             while (($line = $in->readLine()) !== null) {
  389.                 $lines[] = $line;
  390.             }
  391.         }
  392.  
  393.         try {
  394.             foreach ($lines as $line) {
  395.                 $line = trim($line);
  396.                 $line = ProjectConfigurator::replaceProperties($this->project, $line,
  397.                         $this->project->getProperties());
  398.                 
  399.                 if (StringHelper::startsWith("//", $line) || 
  400.                     StringHelper::startsWith("--", $line) ||
  401.                     StringHelper::startsWith("#", $line)) {
  402.                     continue;
  403.                 }
  404.                 
  405.                 if (strlen($line) > 4
  406.                         && strtoupper(substr($line,0, 4)) == "REM ") {
  407.                     continue;
  408.                 }
  409.  
  410.                 $sql .= " " . $line;
  411.                 $sql = trim($sql);
  412.  
  413.                 // SQL defines "--" as a comment to EOL
  414.                 // and in Oracle it may contain a hint
  415.                 // so we cannot just remove it, instead we must end it
  416.                 if (strpos($line, "--") !== false) {
  417.                     $sql .= "\n";
  418.                 }
  419.  
  420.                 if ($this->delimiterType == self::DELIM_NORMAL
  421.                         && StringHelper::endsWith($this->delimiter, $sql)
  422.                         || $this->delimiterType == self::DELIM_ROW
  423.                         && $line == $this->delimiter) {
  424.                     $this->log("SQL: " . $sql, Project::MSG_VERBOSE);
  425.                     $this->execSQL(StringHelper::substring($sql, 0, strlen($sql) - strlen($this->delimiter)), $out);
  426.                     $sql = "";
  427.                 }
  428.             }
  429.  
  430.             // Catch any statements not followed by ;
  431.             if ($sql !== "") {
  432.                 $this->execSQL($sql, $out);
  433.             }
  434.         } catch (SQLException $e) {
  435.             throw new BuildException("Error running statements", $e);
  436.         }
  437.     }
  438.  
  439.         
  440.     /**
  441.      * Exec the sql statement.
  442.      * @throws SQLException 
  443.      */
  444.     protected function execSQL($sql, $out = null) {
  445.         // Check and ignore empty statements
  446.         if (trim($sql) == "") {
  447.             return;
  448.         }
  449.  
  450.         try {
  451.             $this->totalSql++;
  452.             if (!$this->statement->execute($sql)) {
  453.                 $this->log($this->statement->getUpdateCount() . " rows affected", Project::MSG_VERBOSE);
  454.             } else {
  455.                 if ($this->print) {
  456.                     $this->printResults($out);
  457.                 }
  458.             }
  459.             
  460.             $this->goodSql++;
  461.             
  462.         } catch (SQLException $e) {            
  463.             $this->log("Failed to execute: " . $sql, Project::MSG_ERR);
  464.             if ($this->onError != "continue") {            
  465.                 throw new BuildException("Failed to execute SQL", $e);
  466.             }
  467.             $this->log($e->getMessage(), Project::MSG_ERR);
  468.         }
  469.     }
  470.     
  471.     /**
  472.      * print any results in the statement.
  473.      * @throw SQLException
  474.      */
  475.     protected function printResults($out = null) {
  476.         
  477.         $rs = null;        
  478.         do {
  479.             $rs = $this->statement->getResultSet();
  480.             
  481.             if ($rs !== null) {
  482.             
  483.                 $this->log("Processing new result set.", Project::MSG_VERBOSE);            
  484.     
  485.                 $line = "";
  486.  
  487.                 $colsprinted = false;
  488.                 
  489.                 while ($rs->next()) {
  490.                     $fields = $rs->getRow();
  491.                     
  492.                     if (!$colsprinted && $this->showheaders) {
  493.                         $first = true;
  494.                         foreach($fields as $fieldName => $ignore) {
  495.                             if ($first) $first = false; else $line .= ",";
  496.                             $line .= $fieldName;
  497.                         }
  498.                         if ($out !== null) {
  499.                             $out->write($line);
  500.                             $out->newLine();
  501.                         } else {
  502.                             print($line.PHP_EOL);
  503.                         }
  504.                         $line = "";
  505.                         $colsprinted = true;
  506.                     } // if show headers
  507.                     
  508.                     $first = true;
  509.                     foreach($fields as $columnValue) {
  510.                         
  511.                         if ($columnValue != null) {
  512.                             $columnValue = trim($columnValue);
  513.                         }
  514.  
  515.                         if ($first) {
  516.                             $first = false;
  517.                         } else {
  518.                             $line .= ",";
  519.                         }
  520.                         $line .= $columnValue;
  521.                     }
  522.                     
  523.                     if ($out !== null) {
  524.                         $out->write($line);
  525.                         $out->newLine();
  526.                     } else {                    
  527.                         print($line . PHP_EOL);
  528.                     }
  529.                     $line = "";
  530.                     
  531.                 } // while rs->next()
  532.             }
  533.         } while ($this->statement->getMoreResults());
  534.         print(PHP_EOL);
  535.         if ($out !== null) $out->newLine();
  536.     }
  537. }
  538.  
  539.  
  540. /**
  541.  * "Inner" class that contains the definition of a new transaction element.
  542.  * Transactions allow several files or blocks of statements
  543.  * to be executed using the same JDBC connection and commit
  544.  * operation in between.
  545.  */
  546. class SQLExecTransaction {
  547.  
  548.     private $tSrcFile = null;
  549.     private $tSqlCommand = "";
  550.     private $parent;
  551.     
  552.     function __construct($parent)
  553.     {
  554.         // Parent is required so that we can log things ...
  555.         $this->parent = $parent;
  556.     }
  557.     
  558.     public function setSrc(PhingFile $src)
  559.     {
  560.         $this->tSrcFile = $src;
  561.     }
  562.  
  563.     public function addText($sql)
  564.     {
  565.         $this->tSqlCommand .= $sql;
  566.     }
  567.  
  568.     /**
  569.      * @throws IOException, SQLException
  570.      */
  571.     public function runTransaction($out = null)
  572.     {
  573.         if (!empty($this->tSqlCommand)) {
  574.             $this->parent->log("Executing commands", Project::MSG_INFO);
  575.             $this->parent->runStatements(new StringReader($this->tSqlCommand), $out);
  576.         }
  577.  
  578.         if ($this->tSrcFile !== null) {
  579.             $this->parent->log("Executing file: " . $this->tSrcFile->getAbsolutePath(),
  580.                 Project::MSG_INFO);
  581.  
  582.             $reader = new FileReader($this->tSrcFile);
  583.  
  584.             $this->parent->runStatements($reader, $out);
  585.             $reader->close();
  586.         }
  587.     }
  588. }
  589.  
  590.  
  591.