| [ Index ] |
|
Code source de Symfony 1.0.0 |
1 <?php 2 /* 3 * $Id: CreoleSQLExecTask.php 3076 2006-12-18 08:52:12Z fabien $ 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/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 * SQL statement 74 */ 75 private $statement = null; 76 77 /** 78 * SQL input file 79 */ 80 private $srcFile = null; 81 82 /** 83 * SQL input command 84 */ 85 private $sqlCommand = ""; 86 87 /** 88 * SQL transactions to perform 89 */ 90 private $transactions = array(); 91 92 /** 93 * SQL Statement delimiter 94 */ 95 private $delimiter = ";"; 96 97 /** 98 * The delimiter type indicating whether the delimiter will 99 * only be recognized on a line by itself 100 */ 101 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 118 119 /** 120 * Action to perform if an error is found 121 **/ 122 private $onError = "abort"; 123 124 /** 125 * Encoding to use when reading SQL statements from a file 126 */ 127 private $encoding = null; 128 129 /** 130 * Append to an existing file or overwrite it? 131 */ 132 private $append = false; 133 134 /** 135 * Set the name of the SQL file to be run. 136 * Required unless statements are enclosed in the build file 137 */ 138 public function setSrc(PhingFile $srcFile) { 139 $this->srcFile = $srcFile; 140 } 141 142 /** 143 * Set an inline SQL command to execute. 144 * NB: Properties are not expanded in this text. 145 */ 146 public function addText($sql) { 147 $this->sqlCommand .= $sql; 148 } 149 150 /** 151 * Adds a set of files (nested fileset attribute). 152 */ 153 public function addFileset(FileSet $set) { 154 $this->filesets[] = $set; 155 } 156 157 /** 158 * Add a SQL transaction to execute 159 */ 160 public function createTransaction() { 161 $t = new SQLExecTransaction($this); 162 $this->transactions[] = $t; 163 return $t; 164 } 165 166 /** 167 * Set the file encoding to use on the SQL files read in 168 * 169 * @param encoding the encoding to use on the files 170 */ 171 public function setEncoding($encoding) { 172 $this->encoding = $encoding; 173 } 174 175 /** 176 * Set the statement delimiter. 177 * 178 * <p>For example, set this to "go" and delimitertype to "ROW" for 179 * Sybase ASE or MS SQL Server.</p> 180 * 181 * @param delimiter 182 */ 183 public function setDelimiter($delimiter) 184 { 185 $this->delimiter = $delimiter; 186 } 187 188 /** 189 * Set the Delimiter type for this sql task. The delimiter type takes two 190 * values - normal and row. Normal means that any occurence of the delimiter 191 * terminate the SQL command whereas with row, only a line containing just 192 * the delimiter is recognized as the end of the command. 193 * 194 * @param string $delimiterType 195 */ 196 public function setDelimiterType($delimiterType) 197 { 198 $this->delimiterType = $delimiterType; 199 } 200 201 /** 202 * Set the print flag. 203 * 204 * @param boolean $print 205 */ 206 public function setPrint($print) 207 { 208 $this->print = (boolean) $print; 209 } 210 211 /** 212 * Print headers for result sets from the 213 * statements; optional, default true. 214 * @param boolean $showheaders 215 */ 216 public function setShowheaders($showheaders) { 217 $this->showheaders = (boolean) $showheaders; 218 } 219 220 /** 221 * Set the output file; 222 * optional, defaults to the console. 223 * @param PhingFile $output 224 */ 225 public function setOutput(PhingFile $output) { 226 $this->output = $output; 227 } 228 229 /** 230 * whether output should be appended to or overwrite 231 * an existing file. Defaults to false. 232 * @param $append 233 */ 234 public function setAppend($append) { 235 $this->append = (boolean) $append; 236 } 237 238 239 /** 240 * Action to perform when statement fails: continue, stop, or abort 241 * optional; default "abort" 242 */ 243 public function setOnerror($action) { 244 $this->onError = $action; 245 } 246 247 /** 248 * Load the sql file and then execute it 249 * @throws BuildException 250 */ 251 public function main() { 252 253 $savedTransaction = array(); 254 for($i=0,$size=count($this->transactions); $i < $size; $i++) { 255 $savedTransaction[] = clone $this->transactions[$i]; 256 } 257 258 $savedSqlCommand = $this->sqlCommand; 259 260 $this->sqlCommand = trim($this->sqlCommand); 261 262 try { 263 if ($this->srcFile === null && $this->sqlCommand === "" 264 && empty($this->filesets)) { 265 if (count($this->transactions) === 0) { 266 throw new BuildException("Source file or fileset, " 267 . "transactions or sql statement " 268 . "must be set!", $this->location); 269 } 270 } 271 272 if ($this->srcFile !== null && !$this->srcFile->exists()) { 273 throw new BuildException("Source file does not exist!", $this->location); 274 } 275 276 // deal with the filesets 277 for ($i = 0,$size=count($this->filesets); $i < $size; $i++) { 278 $fs = $this->filesets[$i]; 279 $ds = $fs->getDirectoryScanner($this->project); 280 $srcDir = $fs->getDir($this->project); 281 282 $srcFiles = $ds->getIncludedFiles(); 283 284 // Make a transaction for each file 285 for ($j=0, $size=count($srcFiles); $j < $size; $j++) { 286 $t = $this->createTransaction(); 287 $t->setSrc(new PhingFile($srcDir, $srcFiles[$j])); 288 } 289 } 290 291 // Make a transaction group for the outer command 292 $t = $this->createTransaction(); 293 if ($this->srcFile) $t->setSrc($this->srcFile); 294 $t->addText($this->sqlCommand); 295 $this->conn = $this->getConnection(); 296 297 try { 298 299 $this->statement = $this->conn->createStatement(); 300 301 $out = null; 302 303 try { 304 305 if ($this->output !== null) { 306 $this->log("Opening output file " . $this->output, PROJECT_MSG_VERBOSE); 307 $out = new BufferedWriter(new FileWriter($this->output->getAbsolutePath(), $this->append)); 308 } 309 310 // Process all transactions 311 for ($i=0,$size=count($this->transactions); $i < $size; $i++) { 312 $this->transactions[$i]->runTransaction($out); 313 if (!$this->isAutocommit()) { 314 $this->log("Commiting transaction", PROJECT_MSG_VERBOSE); 315 $this->conn->commit(); 316 } 317 } 318 if ($out) $out->close(); 319 } catch (Exception $e) { 320 if ($out) $out->close(); 321 throw $e; 322 } 323 } catch (IOException $e) { 324 if (!$this->isAutocommit() && $this->conn !== null && $this->onError == "abort") { 325 try { 326 $this->conn->rollback(); 327 } catch (SQLException $ex) {} 328 } 329 throw new BuildException($e->getMessage(), $this->location); 330 } catch (SQLException $e){ 331 if (!$this->isAutocommit() && $this->conn !== null && $this->onError == "abort") { 332 try { 333 $this->conn->rollback(); 334 } catch (SQLException $ex) {} 335 } 336 throw new BuildException($e->getMessage(), $this->location); 337 } 338 339 $this->log($this->goodSql . " of " . $this->totalSql . 340 " SQL statements executed successfully"); 341 } catch (Exception $e) { 342 $this->transactions = $savedTransaction; 343 $this->sqlCommand = $savedSqlCommand; 344 throw $e; 345 } 346 // finally { 347 $this->transactions = $savedTransaction; 348 $this->sqlCommand = $savedSqlCommand; 349 350 } 351 352 353 /** 354 * read in lines and execute them 355 * @throws SQLException, IOException 356 */ 357 public function runStatements(Reader $reader, $out = null) { 358 $sql = ""; 359 $line = ""; 360 $in = new BufferedReader($reader); 361 try { 362 while (($line = $in->readLine()) !== null) { 363 $line = trim($line); 364 $line = ProjectConfigurator::replaceProperties($this->project, $line, 365 $this->project->getProperties()); 366 367 if (StringHelper::startsWith("//", $line) || 368 StringHelper::startsWith("--", $line) || 369 StringHelper::startsWith("#", $line)) { 370 continue; 371 } 372 373 if (strlen($line) > 4 374 && strtoupper(substr($line,0, 4)) == "REM ") { 375 continue; 376 } 377 378 $sql .= " " . $line; 379 $sql = trim($sql); 380 381 // SQL defines "--" as a comment to EOL 382 // and in Oracle it may contain a hint 383 // so we cannot just remove it, instead we must end it 384 if (strpos($line, "--") !== false) { 385 $sql .= "\n"; 386 } 387 388 if ($this->delimiterType == self::DELIM_NORMAL 389 && StringHelper::endsWith($this->delimiter, $sql) 390 || $this->delimiterType == self::DELIM_ROW 391 && $line == $this->delimiter) { 392 $this->log("SQL: " . $sql, PROJECT_MSG_VERBOSE); 393 $this->execSQL(StringHelper::substring($sql, 0, strlen($sql) - strlen($this->delimiter) - 1), $out); 394 $sql = ""; 395 } 396 } 397 398 // Catch any statements not followed by ; 399 if ($sql !== "") { 400 $this->execSQL($sql, $out); 401 } 402 } catch (SQLException $e) { 403 throw new BuildException("Error running statements", $e); 404 } 405 } 406 407 408 /** 409 * Exec the sql statement. 410 * @throws SQLException 411 */ 412 protected function execSQL($sql, $out = null) { 413 // Check and ignore empty statements 414 if (trim($sql) == "") { 415 return; 416 } 417 418 try { 419 $this->totalSql++; 420 if (!$this->statement->execute($sql)) { 421 $this->log($this->statement->getUpdateCount() . " rows affected", PROJECT_MSG_VERBOSE); 422 } else { 423 if ($this->print) { 424 $this->printResults($out); 425 } 426 } 427 428 $this->goodSql++; 429 430 } catch (SQLException $e) { 431 $this->log("Failed to execute: " . $sql, PROJECT_MSG_ERR); 432 if ($this->onError != "continue") { 433 throw new BuildException("Failed to execute SQL", $e); 434 } 435 $this->log($e->getMessage(), PROJECT_MSG_ERR); 436 } 437 } 438 439 /** 440 * print any results in the statement. 441 * @throw SQLException 442 */ 443 protected function printResults($out = null) { 444 $lSep = Phing::getProperty('line.separator'); 445 $rs = null; 446 do { 447 $rs = $this->statement->getResultSet(); 448 449 if ($rs !== null) { 450 451 $this->log("Processing new result set.", PROJECT_MSG_VERBOSE); 452 453 $line = ""; 454 455 $colsprinted = false; 456 457 while ($rs->next()) { 458 $fields = $rs->getRow(); 459 460 if (!$colsprinted && $this->showheaders) { 461 $first = true; 462 foreach($fields as $fieldName => $ignore) { 463 if ($first) $first = false; else $line .= ","; 464 $line .= $fieldName; 465 } 466 if ($out !== null) { 467 $out->write($line); 468 $out->newLine(); 469 } else { 470 print($line.$lSep); 471 } 472 $line = ""; 473 $colsprinted = true; 474 } // if show headers 475 476 $first = true; 477 foreach($fields as $columnValue) { 478 479 if ($columnValue != null) { 480 $columnValue = trim($columnValue); 481 } 482 483 if ($first) { 484 $first = false; 485 } else { 486 $line .= ","; 487 } 488 $line .= $columnValue; 489 } 490 491 if ($out !== null) { 492 $out->write($line); 493 $out->newLine(); 494 } else { 495 print($line . $lSep); 496 } 497 $line = ""; 498 499 } // while rs->next() 500 } 501 } while ($this->statement->getMoreResults()); 502 print($lSep); 503 if ($out !== null) $out->newLine(); 504 } 505 } 506 507 508 /** 509 * "Inner" class that contains the definition of a new transaction element. 510 * Transactions allow several files or blocks of statements 511 * to be executed using the same JDBC connection and commit 512 * operation in between. 513 */ 514 class SQLExecTransaction { 515 516 private $tSrcFile = null; 517 private $tSqlCommand = ""; 518 private $parent; 519 520 function __construct($parent) 521 { 522 // Parent is required so that we can log things ... 523 $this->parent = $parent; 524 } 525 526 public function setSrc(PhingFile $src) 527 { 528 $this->tSrcFile = $src; 529 } 530 531 public function addText($sql) 532 { 533 $this->tSqlCommand .= $sql; 534 } 535 536 /** 537 * @throws IOException, SQLException 538 */ 539 public function runTransaction($out = null) 540 { 541 if (!empty($this->tSqlCommand)) { 542 $this->parent->log("Executing commands", PROJECT_MSG_INFO); 543 $this->parent->runStatements(new StringReader($this->tSqlCommand), $out); 544 } 545 546 if ($this->tSrcFile !== null) { 547 $this->parent->log("Executing file: " . $this->tSrcFile->getAbsolutePath(), 548 PROJECT_MSG_INFO); 549 $reader = new FileReader($this->tSrcFile); 550 $this->parent->runStatements($reader, $out); 551 $reader->close(); 552 } 553 } 554 } 555 556
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Fri Mar 16 22:42:14 2007 | par Balluche grâce à PHPXref 0.7 |