[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * This is the Oracle database abstraction layer. 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 * http://www.gnu.org/copyleft/gpl.html 19 * 20 * @file 21 * @ingroup Database 22 */ 23 24 /** 25 * The oci8 extension is fairly weak and doesn't support oci_num_rows, among 26 * other things. We use a wrapper class to handle that and other 27 * Oracle-specific bits, like converting column names back to lowercase. 28 * @ingroup Database 29 */ 30 class ORAResult { 31 private $rows; 32 private $cursor; 33 private $nrows; 34 35 private $columns = array(); 36 37 private function array_unique_md( $array_in ) { 38 $array_out = array(); 39 $array_hashes = array(); 40 41 foreach ( $array_in as $item ) { 42 $hash = md5( serialize( $item ) ); 43 if ( !isset( $array_hashes[$hash] ) ) { 44 $array_hashes[$hash] = $hash; 45 $array_out[] = $item; 46 } 47 } 48 49 return $array_out; 50 } 51 52 /** 53 * @param DatabaseBase $db 54 * @param resource $stmt A valid OCI statement identifier 55 * @param bool $unique 56 */ 57 function __construct( &$db, $stmt, $unique = false ) { 58 $this->db =& $db; 59 60 $this->nrows = oci_fetch_all( $stmt, $this->rows, 0, -1, OCI_FETCHSTATEMENT_BY_ROW | OCI_NUM ); 61 if ( $this->nrows === false ) { 62 $e = oci_error( $stmt ); 63 $db->reportQueryError( $e['message'], $e['code'], '', __METHOD__ ); 64 $this->free(); 65 66 return; 67 } 68 69 if ( $unique ) { 70 $this->rows = $this->array_unique_md( $this->rows ); 71 $this->nrows = count( $this->rows ); 72 } 73 74 if ( $this->nrows > 0 ) { 75 foreach ( $this->rows[0] as $k => $v ) { 76 $this->columns[$k] = strtolower( oci_field_name( $stmt, $k + 1 ) ); 77 } 78 } 79 80 $this->cursor = 0; 81 oci_free_statement( $stmt ); 82 } 83 84 public function free() { 85 unset( $this->db ); 86 } 87 88 public function seek( $row ) { 89 $this->cursor = min( $row, $this->nrows ); 90 } 91 92 public function numRows() { 93 return $this->nrows; 94 } 95 96 public function numFields() { 97 return count( $this->columns ); 98 } 99 100 public function fetchObject() { 101 if ( $this->cursor >= $this->nrows ) { 102 return false; 103 } 104 $row = $this->rows[$this->cursor++]; 105 $ret = new stdClass(); 106 foreach ( $row as $k => $v ) { 107 $lc = $this->columns[$k]; 108 $ret->$lc = $v; 109 } 110 111 return $ret; 112 } 113 114 public function fetchRow() { 115 if ( $this->cursor >= $this->nrows ) { 116 return false; 117 } 118 119 $row = $this->rows[$this->cursor++]; 120 $ret = array(); 121 foreach ( $row as $k => $v ) { 122 $lc = $this->columns[$k]; 123 $ret[$lc] = $v; 124 $ret[$k] = $v; 125 } 126 127 return $ret; 128 } 129 } 130 131 /** 132 * Utility class. 133 * @ingroup Database 134 */ 135 class ORAField implements Field { 136 private $name, $tablename, $default, $max_length, $nullable, 137 $is_pk, $is_unique, $is_multiple, $is_key, $type; 138 139 function __construct( $info ) { 140 $this->name = $info['column_name']; 141 $this->tablename = $info['table_name']; 142 $this->default = $info['data_default']; 143 $this->max_length = $info['data_length']; 144 $this->nullable = $info['not_null']; 145 $this->is_pk = isset( $info['prim'] ) && $info['prim'] == 1 ? 1 : 0; 146 $this->is_unique = isset( $info['uniq'] ) && $info['uniq'] == 1 ? 1 : 0; 147 $this->is_multiple = isset( $info['nonuniq'] ) && $info['nonuniq'] == 1 ? 1 : 0; 148 $this->is_key = ( $this->is_pk || $this->is_unique || $this->is_multiple ); 149 $this->type = $info['data_type']; 150 } 151 152 function name() { 153 return $this->name; 154 } 155 156 function tableName() { 157 return $this->tablename; 158 } 159 160 function defaultValue() { 161 return $this->default; 162 } 163 164 function maxLength() { 165 return $this->max_length; 166 } 167 168 function isNullable() { 169 return $this->nullable; 170 } 171 172 function isKey() { 173 return $this->is_key; 174 } 175 176 function isMultipleKey() { 177 return $this->is_multiple; 178 } 179 180 function type() { 181 return $this->type; 182 } 183 } 184 185 /** 186 * @ingroup Database 187 */ 188 class DatabaseOracle extends DatabaseBase { 189 /** @var resource */ 190 protected $mLastResult = null; 191 192 /** @var int The number of rows affected as an integer */ 193 protected $mAffectedRows; 194 195 /** @var int */ 196 private $mInsertId = null; 197 198 /** @var bool */ 199 private $ignoreDupValOnIndex = false; 200 201 /** @var bool|array */ 202 private $sequenceData = null; 203 204 /** @var string Character set for Oracle database */ 205 private $defaultCharset = 'AL32UTF8'; 206 207 /** @var array */ 208 private $mFieldInfoCache = array(); 209 210 function __construct( $p = null ) { 211 global $wgDBprefix; 212 213 if ( !is_array( $p ) ) { // legacy calling pattern 214 wfDeprecated( __METHOD__ . " method called without parameter array.", "1.22" ); 215 $args = func_get_args(); 216 $p = array( 217 'host' => isset( $args[0] ) ? $args[0] : false, 218 'user' => isset( $args[1] ) ? $args[1] : false, 219 'password' => isset( $args[2] ) ? $args[2] : false, 220 'dbname' => isset( $args[3] ) ? $args[3] : false, 221 'flags' => isset( $args[4] ) ? $args[4] : 0, 222 'tablePrefix' => isset( $args[5] ) ? $args[5] : 'get from global', 223 'schema' => 'get from global', 224 'foreign' => isset( $args[6] ) ? $args[6] : false 225 ); 226 } 227 if ( $p['tablePrefix'] == 'get from global' ) { 228 $p['tablePrefix'] = $wgDBprefix; 229 } 230 $p['tablePrefix'] = strtoupper( $p['tablePrefix'] ); 231 parent::__construct( $p ); 232 wfRunHooks( 'DatabaseOraclePostInit', array( $this ) ); 233 } 234 235 function __destruct() { 236 if ( $this->mOpened ) { 237 wfSuppressWarnings(); 238 $this->close(); 239 wfRestoreWarnings(); 240 } 241 } 242 243 function getType() { 244 return 'oracle'; 245 } 246 247 function cascadingDeletes() { 248 return true; 249 } 250 251 function cleanupTriggers() { 252 return true; 253 } 254 255 function strictIPs() { 256 return true; 257 } 258 259 function realTimestamps() { 260 return true; 261 } 262 263 function implicitGroupby() { 264 return false; 265 } 266 267 function implicitOrderby() { 268 return false; 269 } 270 271 function searchableIPs() { 272 return true; 273 } 274 275 /** 276 * Usually aborts on failure 277 * @param string $server 278 * @param string $user 279 * @param string $password 280 * @param string $dbName 281 * @throws DBConnectionError 282 * @return DatabaseBase|null 283 */ 284 function open( $server, $user, $password, $dbName ) { 285 global $wgDBOracleDRCP; 286 if ( !function_exists( 'oci_connect' ) ) { 287 throw new DBConnectionError( 288 $this, 289 "Oracle functions missing, have you compiled PHP with the --with-oci8 option?\n " . 290 "(Note: if you recently installed PHP, you may need to restart your webserver\n " . 291 "and database)\n" ); 292 } 293 294 $this->close(); 295 $this->mUser = $user; 296 $this->mPassword = $password; 297 // changed internal variables functions 298 // mServer now holds the TNS endpoint 299 // mDBname is schema name if different from username 300 if ( !$server ) { 301 // backward compatibillity (server used to be null and TNS was supplied in dbname) 302 $this->mServer = $dbName; 303 $this->mDBname = $user; 304 } else { 305 $this->mServer = $server; 306 if ( !$dbName ) { 307 $this->mDBname = $user; 308 } else { 309 $this->mDBname = $dbName; 310 } 311 } 312 313 if ( !strlen( $user ) ) { # e.g. the class is being loaded 314 return null; 315 } 316 317 if ( $wgDBOracleDRCP ) { 318 $this->setFlag( DBO_PERSISTENT ); 319 } 320 321 $session_mode = $this->mFlags & DBO_SYSDBA ? OCI_SYSDBA : OCI_DEFAULT; 322 323 wfSuppressWarnings(); 324 if ( $this->mFlags & DBO_PERSISTENT ) { 325 $this->mConn = oci_pconnect( 326 $this->mUser, 327 $this->mPassword, 328 $this->mServer, 329 $this->defaultCharset, 330 $session_mode 331 ); 332 } elseif ( $this->mFlags & DBO_DEFAULT ) { 333 $this->mConn = oci_new_connect( 334 $this->mUser, 335 $this->mPassword, 336 $this->mServer, 337 $this->defaultCharset, 338 $session_mode 339 ); 340 } else { 341 $this->mConn = oci_connect( 342 $this->mUser, 343 $this->mPassword, 344 $this->mServer, 345 $this->defaultCharset, 346 $session_mode 347 ); 348 } 349 wfRestoreWarnings(); 350 351 if ( $this->mUser != $this->mDBname ) { 352 //change current schema in session 353 $this->selectDB( $this->mDBname ); 354 } 355 356 if ( !$this->mConn ) { 357 throw new DBConnectionError( $this, $this->lastError() ); 358 } 359 360 $this->mOpened = true; 361 362 # removed putenv calls because they interfere with the system globaly 363 $this->doQuery( 'ALTER SESSION SET NLS_TIMESTAMP_FORMAT=\'DD-MM-YYYY HH24:MI:SS.FF6\'' ); 364 $this->doQuery( 'ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT=\'DD-MM-YYYY HH24:MI:SS.FF6\'' ); 365 $this->doQuery( 'ALTER SESSION SET NLS_NUMERIC_CHARACTERS=\'.,\'' ); 366 367 return $this->mConn; 368 } 369 370 /** 371 * Closes a database connection, if it is open 372 * Returns success, true if already closed 373 * @return bool 374 */ 375 protected function closeConnection() { 376 return oci_close( $this->mConn ); 377 } 378 379 function execFlags() { 380 return $this->mTrxLevel ? OCI_NO_AUTO_COMMIT : OCI_COMMIT_ON_SUCCESS; 381 } 382 383 protected function doQuery( $sql ) { 384 wfDebug( "SQL: [$sql]\n" ); 385 if ( !StringUtils::isUtf8( $sql ) ) { 386 throw new MWException( "SQL encoding is invalid\n$sql" ); 387 } 388 389 // handle some oracle specifics 390 // remove AS column/table/subquery namings 391 if ( !$this->getFlag( DBO_DDLMODE ) ) { 392 $sql = preg_replace( '/ as /i', ' ', $sql ); 393 } 394 395 // Oracle has issues with UNION clause if the statement includes LOB fields 396 // So we do a UNION ALL and then filter the results array with array_unique 397 $union_unique = ( preg_match( '/\/\* UNION_UNIQUE \*\/ /', $sql ) != 0 ); 398 // EXPLAIN syntax in Oracle is EXPLAIN PLAN FOR and it return nothing 399 // you have to select data from plan table after explain 400 $explain_id = MWTimestamp::getLocalInstance()->format( 'dmYHis' ); 401 402 $sql = preg_replace( 403 '/^EXPLAIN /', 404 'EXPLAIN PLAN SET STATEMENT_ID = \'' . $explain_id . '\' FOR', 405 $sql, 406 1, 407 $explain_count 408 ); 409 410 wfSuppressWarnings(); 411 412 if ( ( $this->mLastResult = $stmt = oci_parse( $this->mConn, $sql ) ) === false ) { 413 $e = oci_error( $this->mConn ); 414 $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ ); 415 416 return false; 417 } 418 419 if ( !oci_execute( $stmt, $this->execFlags() ) ) { 420 $e = oci_error( $stmt ); 421 if ( !$this->ignoreDupValOnIndex || $e['code'] != '1' ) { 422 $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ ); 423 424 return false; 425 } 426 } 427 428 wfRestoreWarnings(); 429 430 if ( $explain_count > 0 ) { 431 return $this->doQuery( 'SELECT id, cardinality "ROWS" FROM plan_table ' . 432 'WHERE statement_id = \'' . $explain_id . '\'' ); 433 } elseif ( oci_statement_type( $stmt ) == 'SELECT' ) { 434 return new ORAResult( $this, $stmt, $union_unique ); 435 } else { 436 $this->mAffectedRows = oci_num_rows( $stmt ); 437 438 return true; 439 } 440 } 441 442 function queryIgnore( $sql, $fname = '' ) { 443 return $this->query( $sql, $fname, true ); 444 } 445 446 /** 447 * Frees resources associated with the LOB descriptor 448 * @param ResultWrapper|resource $res 449 */ 450 function freeResult( $res ) { 451 if ( $res instanceof ResultWrapper ) { 452 $res = $res->result; 453 } 454 455 $res->free(); 456 } 457 458 /** 459 * @param ResultWrapper|stdClass $res 460 * @return mixed 461 */ 462 function fetchObject( $res ) { 463 if ( $res instanceof ResultWrapper ) { 464 $res = $res->result; 465 } 466 467 return $res->fetchObject(); 468 } 469 470 function fetchRow( $res ) { 471 if ( $res instanceof ResultWrapper ) { 472 $res = $res->result; 473 } 474 475 return $res->fetchRow(); 476 } 477 478 function numRows( $res ) { 479 if ( $res instanceof ResultWrapper ) { 480 $res = $res->result; 481 } 482 483 return $res->numRows(); 484 } 485 486 function numFields( $res ) { 487 if ( $res instanceof ResultWrapper ) { 488 $res = $res->result; 489 } 490 491 return $res->numFields(); 492 } 493 494 function fieldName( $stmt, $n ) { 495 return oci_field_name( $stmt, $n ); 496 } 497 498 /** 499 * This must be called after nextSequenceVal 500 * @return null|int 501 */ 502 function insertId() { 503 return $this->mInsertId; 504 } 505 506 /** 507 * @param mixed $res 508 * @param int $row 509 */ 510 function dataSeek( $res, $row ) { 511 if ( $res instanceof ORAResult ) { 512 $res->seek( $row ); 513 } else { 514 $res->result->seek( $row ); 515 } 516 } 517 518 function lastError() { 519 if ( $this->mConn === false ) { 520 $e = oci_error(); 521 } else { 522 $e = oci_error( $this->mConn ); 523 } 524 525 return $e['message']; 526 } 527 528 function lastErrno() { 529 if ( $this->mConn === false ) { 530 $e = oci_error(); 531 } else { 532 $e = oci_error( $this->mConn ); 533 } 534 535 return $e['code']; 536 } 537 538 function affectedRows() { 539 return $this->mAffectedRows; 540 } 541 542 /** 543 * Returns information about an index 544 * If errors are explicitly ignored, returns NULL on failure 545 * @param string $table 546 * @param string $index 547 * @param string $fname 548 * @return bool 549 */ 550 function indexInfo( $table, $index, $fname = __METHOD__ ) { 551 return false; 552 } 553 554 function indexUnique( $table, $index, $fname = __METHOD__ ) { 555 return false; 556 } 557 558 function insert( $table, $a, $fname = __METHOD__, $options = array() ) { 559 if ( !count( $a ) ) { 560 return true; 561 } 562 563 if ( !is_array( $options ) ) { 564 $options = array( $options ); 565 } 566 567 if ( in_array( 'IGNORE', $options ) ) { 568 $this->ignoreDupValOnIndex = true; 569 } 570 571 if ( !is_array( reset( $a ) ) ) { 572 $a = array( $a ); 573 } 574 575 foreach ( $a as &$row ) { 576 $this->insertOneRow( $table, $row, $fname ); 577 } 578 $retVal = true; 579 580 if ( in_array( 'IGNORE', $options ) ) { 581 $this->ignoreDupValOnIndex = false; 582 } 583 584 return $retVal; 585 } 586 587 private function fieldBindStatement( $table, $col, &$val, $includeCol = false ) { 588 $col_info = $this->fieldInfoMulti( $table, $col ); 589 $col_type = $col_info != false ? $col_info->type() : 'CONSTANT'; 590 591 $bind = ''; 592 if ( is_numeric( $col ) ) { 593 $bind = $val; 594 $val = null; 595 596 return $bind; 597 } elseif ( $includeCol ) { 598 $bind = "$col = "; 599 } 600 601 if ( $val == '' && $val !== 0 && $col_type != 'BLOB' && $col_type != 'CLOB' ) { 602 $val = null; 603 } 604 605 if ( $val === 'NULL' ) { 606 $val = null; 607 } 608 609 if ( $val === null ) { 610 if ( $col_info != false && $col_info->isNullable() == 0 && $col_info->defaultValue() != null ) { 611 $bind .= 'DEFAULT'; 612 } else { 613 $bind .= 'NULL'; 614 } 615 } else { 616 $bind .= ':' . $col; 617 } 618 619 return $bind; 620 } 621 622 /** 623 * @param string $table 624 * @param array $row 625 * @param string $fname 626 * @return bool 627 * @throws DBUnexpectedError 628 */ 629 private function insertOneRow( $table, $row, $fname ) { 630 global $wgContLang; 631 632 $table = $this->tableName( $table ); 633 // "INSERT INTO tables (a, b, c)" 634 $sql = "INSERT INTO " . $table . " (" . join( ',', array_keys( $row ) ) . ')'; 635 $sql .= " VALUES ("; 636 637 // for each value, append ":key" 638 $first = true; 639 foreach ( $row as $col => &$val ) { 640 if ( !$first ) { 641 $sql .= ', '; 642 } else { 643 $first = false; 644 } 645 if ( $this->isQuotedIdentifier( $val ) ) { 646 $sql .= $this->removeIdentifierQuotes( $val ); 647 unset( $row[$col] ); 648 } else { 649 $sql .= $this->fieldBindStatement( $table, $col, $val ); 650 } 651 } 652 $sql .= ')'; 653 654 if ( ( $this->mLastResult = $stmt = oci_parse( $this->mConn, $sql ) ) === false ) { 655 $e = oci_error( $this->mConn ); 656 $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ ); 657 658 return false; 659 } 660 foreach ( $row as $col => &$val ) { 661 $col_info = $this->fieldInfoMulti( $table, $col ); 662 $col_type = $col_info != false ? $col_info->type() : 'CONSTANT'; 663 664 if ( $val === null ) { 665 // do nothing ... null was inserted in statement creation 666 } elseif ( $col_type != 'BLOB' && $col_type != 'CLOB' ) { 667 if ( is_object( $val ) ) { 668 $val = $val->fetch(); 669 } 670 671 // backward compatibility 672 if ( preg_match( '/^timestamp.*/i', $col_type ) == 1 && strtolower( $val ) == 'infinity' ) { 673 $val = $this->getInfinity(); 674 } 675 676 $val = ( $wgContLang != null ) ? $wgContLang->checkTitleEncoding( $val ) : $val; 677 if ( oci_bind_by_name( $stmt, ":$col", $val, -1, SQLT_CHR ) === false ) { 678 $e = oci_error( $stmt ); 679 $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ ); 680 681 return false; 682 } 683 } else { 684 /** @var OCI_Lob[] $lob */ 685 if ( ( $lob[$col] = oci_new_descriptor( $this->mConn, OCI_D_LOB ) ) === false ) { 686 $e = oci_error( $stmt ); 687 throw new DBUnexpectedError( $this, "Cannot create LOB descriptor: " . $e['message'] ); 688 } 689 690 if ( is_object( $val ) ) { 691 $val = $val->fetch(); 692 } 693 694 if ( $col_type == 'BLOB' ) { 695 $lob[$col]->writeTemporary( $val, OCI_TEMP_BLOB ); 696 oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, OCI_B_BLOB ); 697 } else { 698 $lob[$col]->writeTemporary( $val, OCI_TEMP_CLOB ); 699 oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, OCI_B_CLOB ); 700 } 701 } 702 } 703 704 wfSuppressWarnings(); 705 706 if ( oci_execute( $stmt, $this->execFlags() ) === false ) { 707 $e = oci_error( $stmt ); 708 if ( !$this->ignoreDupValOnIndex || $e['code'] != '1' ) { 709 $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ ); 710 711 return false; 712 } else { 713 $this->mAffectedRows = oci_num_rows( $stmt ); 714 } 715 } else { 716 $this->mAffectedRows = oci_num_rows( $stmt ); 717 } 718 719 wfRestoreWarnings(); 720 721 if ( isset( $lob ) ) { 722 foreach ( $lob as $lob_v ) { 723 $lob_v->free(); 724 } 725 } 726 727 if ( !$this->mTrxLevel ) { 728 oci_commit( $this->mConn ); 729 } 730 731 return oci_free_statement( $stmt ); 732 } 733 734 function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__, 735 $insertOptions = array(), $selectOptions = array() 736 ) { 737 $destTable = $this->tableName( $destTable ); 738 if ( !is_array( $selectOptions ) ) { 739 $selectOptions = array( $selectOptions ); 740 } 741 list( $startOpts, $useIndex, $tailOpts ) = $this->makeSelectOptions( $selectOptions ); 742 if ( is_array( $srcTable ) ) { 743 $srcTable = implode( ',', array_map( array( &$this, 'tableName' ), $srcTable ) ); 744 } else { 745 $srcTable = $this->tableName( $srcTable ); 746 } 747 748 if ( ( $sequenceData = $this->getSequenceData( $destTable ) ) !== false && 749 !isset( $varMap[$sequenceData['column']] ) 750 ) { 751 $varMap[$sequenceData['column']] = 'GET_SEQUENCE_VALUE(\'' . $sequenceData['sequence'] . '\')'; 752 } 753 754 // count-alias subselect fields to avoid abigious definition errors 755 $i = 0; 756 foreach ( $varMap as &$val ) { 757 $val = $val . ' field' . ( $i++ ); 758 } 759 760 $sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' . 761 " SELECT $startOpts " . implode( ',', $varMap ) . 762 " FROM $srcTable $useIndex "; 763 if ( $conds != '*' ) { 764 $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND ); 765 } 766 $sql .= " $tailOpts"; 767 768 if ( in_array( 'IGNORE', $insertOptions ) ) { 769 $this->ignoreDupValOnIndex = true; 770 } 771 772 $retval = $this->query( $sql, $fname ); 773 774 if ( in_array( 'IGNORE', $insertOptions ) ) { 775 $this->ignoreDupValOnIndex = false; 776 } 777 778 return $retval; 779 } 780 781 public function upsert( $table, array $rows, array $uniqueIndexes, array $set, 782 $fname = __METHOD__ 783 ) { 784 if ( !count( $rows ) ) { 785 return true; // nothing to do 786 } 787 788 if ( !is_array( reset( $rows ) ) ) { 789 $rows = array( $rows ); 790 } 791 792 $sequenceData = $this->getSequenceData( $table ); 793 if ( $sequenceData !== false ) { 794 // add sequence column to each list of columns, when not set 795 foreach ( $rows as &$row ) { 796 if ( !isset( $row[$sequenceData['column']] ) ) { 797 $row[$sequenceData['column']] = 798 $this->addIdentifierQuotes( 'GET_SEQUENCE_VALUE(\'' . 799 $sequenceData['sequence'] . '\')' ); 800 } 801 } 802 } 803 804 return parent::upsert( $table, $rows, $uniqueIndexes, $set, $fname ); 805 } 806 807 function tableName( $name, $format = 'quoted' ) { 808 /* 809 Replace reserved words with better ones 810 Using uppercase because that's the only way Oracle can handle 811 quoted tablenames 812 */ 813 switch ( $name ) { 814 case 'user': 815 $name = 'MWUSER'; 816 break; 817 case 'text': 818 $name = 'PAGECONTENT'; 819 break; 820 } 821 822 return strtoupper( parent::tableName( $name, $format ) ); 823 } 824 825 function tableNameInternal( $name ) { 826 $name = $this->tableName( $name ); 827 828 return preg_replace( '/.*\.(.*)/', '$1', $name ); 829 } 830 831 /** 832 * Return the next in a sequence, save the value for retrieval via insertId() 833 * 834 * @param string $seqName 835 * @return null|int 836 */ 837 function nextSequenceValue( $seqName ) { 838 $res = $this->query( "SELECT $seqName.nextval FROM dual" ); 839 $row = $this->fetchRow( $res ); 840 $this->mInsertId = $row[0]; 841 842 return $this->mInsertId; 843 } 844 845 /** 846 * Return sequence_name if table has a sequence 847 * 848 * @param string $table 849 * @return bool 850 */ 851 private function getSequenceData( $table ) { 852 if ( $this->sequenceData == null ) { 853 $result = $this->doQuery( "SELECT lower(asq.sequence_name), 854 lower(atc.table_name), 855 lower(atc.column_name) 856 FROM all_sequences asq, all_tab_columns atc 857 WHERE decode( 858 atc.table_name, 859 '{$this->mTablePrefix}MWUSER', 860 '{$this->mTablePrefix}USER', 861 atc.table_name 862 ) || '_' || 863 atc.column_name || '_SEQ' = '{$this->mTablePrefix}' || asq.sequence_name 864 AND asq.sequence_owner = upper('{$this->mDBname}') 865 AND atc.owner = upper('{$this->mDBname}')" ); 866 867 while ( ( $row = $result->fetchRow() ) !== false ) { 868 $this->sequenceData[$row[1]] = array( 869 'sequence' => $row[0], 870 'column' => $row[2] 871 ); 872 } 873 } 874 $table = strtolower( $this->removeIdentifierQuotes( $this->tableName( $table ) ) ); 875 876 return ( isset( $this->sequenceData[$table] ) ) ? $this->sequenceData[$table] : false; 877 } 878 879 /** 880 * Returns the size of a text field, or -1 for "unlimited" 881 * 882 * @param string $table 883 * @param string $field 884 * @return mixed 885 */ 886 function textFieldSize( $table, $field ) { 887 $fieldInfoData = $this->fieldInfo( $table, $field ); 888 889 return $fieldInfoData->maxLength(); 890 } 891 892 function limitResult( $sql, $limit, $offset = false ) { 893 if ( $offset === false ) { 894 $offset = 0; 895 } 896 897 return "SELECT * FROM ($sql) WHERE rownum >= (1 + $offset) AND rownum < (1 + $limit + $offset)"; 898 } 899 900 function encodeBlob( $b ) { 901 return new Blob( $b ); 902 } 903 904 function decodeBlob( $b ) { 905 if ( $b instanceof Blob ) { 906 $b = $b->fetch(); 907 } 908 909 return $b; 910 } 911 912 function unionQueries( $sqls, $all ) { 913 $glue = ' UNION ALL '; 914 915 return 'SELECT * ' . ( $all ? '' : '/* UNION_UNIQUE */ ' ) . 916 'FROM (' . implode( $glue, $sqls ) . ')'; 917 } 918 919 function wasDeadlock() { 920 return $this->lastErrno() == 'OCI-00060'; 921 } 922 923 function duplicateTableStructure( $oldName, $newName, $temporary = false, 924 $fname = __METHOD__ 925 ) { 926 $temporary = $temporary ? 'TRUE' : 'FALSE'; 927 928 $newName = strtoupper( $newName ); 929 $oldName = strtoupper( $oldName ); 930 931 $tabName = substr( $newName, strlen( $this->mTablePrefix ) ); 932 $oldPrefix = substr( $oldName, 0, strlen( $oldName ) - strlen( $tabName ) ); 933 $newPrefix = strtoupper( $this->mTablePrefix ); 934 935 return $this->doQuery( "BEGIN DUPLICATE_TABLE( '$tabName', " . 936 "'$oldPrefix', '$newPrefix', $temporary ); END;" ); 937 } 938 939 function listTables( $prefix = null, $fname = __METHOD__ ) { 940 $listWhere = ''; 941 if ( !empty( $prefix ) ) { 942 $listWhere = ' AND table_name LIKE \'' . strtoupper( $prefix ) . '%\''; 943 } 944 945 $owner = strtoupper( $this->mDBname ); 946 $result = $this->doQuery( "SELECT table_name FROM all_tables " . 947 "WHERE owner='$owner' AND table_name NOT LIKE '%!_IDX\$_' ESCAPE '!' $listWhere" ); 948 949 // dirty code ... i know 950 $endArray = array(); 951 $endArray[] = strtoupper( $prefix . 'MWUSER' ); 952 $endArray[] = strtoupper( $prefix . 'PAGE' ); 953 $endArray[] = strtoupper( $prefix . 'IMAGE' ); 954 $fixedOrderTabs = $endArray; 955 while ( ( $row = $result->fetchRow() ) !== false ) { 956 if ( !in_array( $row['table_name'], $fixedOrderTabs ) ) { 957 $endArray[] = $row['table_name']; 958 } 959 } 960 961 return $endArray; 962 } 963 964 public function dropTable( $tableName, $fName = __METHOD__ ) { 965 $tableName = $this->tableName( $tableName ); 966 if ( !$this->tableExists( $tableName ) ) { 967 return false; 968 } 969 970 return $this->doQuery( "DROP TABLE $tableName CASCADE CONSTRAINTS PURGE" ); 971 } 972 973 function timestamp( $ts = 0 ) { 974 return wfTimestamp( TS_ORACLE, $ts ); 975 } 976 977 /** 978 * Return aggregated value function call 979 * 980 * @param array $valuedata 981 * @param string $valuename 982 * @return mixed 983 */ 984 public function aggregateValue( $valuedata, $valuename = 'value' ) { 985 return $valuedata; 986 } 987 988 function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) { 989 # Ignore errors during error handling to avoid infinite 990 # recursion 991 $ignore = $this->ignoreErrors( true ); 992 ++$this->mErrorCount; 993 994 if ( $ignore || $tempIgnore ) { 995 wfDebug( "SQL ERROR (ignored): $error\n" ); 996 $this->ignoreErrors( $ignore ); 997 } else { 998 throw new DBQueryError( $this, $error, $errno, $sql, $fname ); 999 } 1000 } 1001 1002 /** 1003 * @return string Wikitext of a link to the server software's web site 1004 */ 1005 public function getSoftwareLink() { 1006 return '[{{int:version-db-oracle-url}} Oracle]'; 1007 } 1008 1009 /** 1010 * @return string Version information from the database 1011 */ 1012 function getServerVersion() { 1013 //better version number, fallback on driver 1014 $rset = $this->doQuery( 1015 'SELECT version FROM product_component_version ' . 1016 'WHERE UPPER(product) LIKE \'ORACLE DATABASE%\'' 1017 ); 1018 if ( !( $row = $rset->fetchRow() ) ) { 1019 return oci_server_version( $this->mConn ); 1020 } 1021 1022 return $row['version']; 1023 } 1024 1025 /** 1026 * Query whether a given index exists 1027 * @param string $table 1028 * @param string $index 1029 * @param string $fname 1030 * @return bool 1031 */ 1032 function indexExists( $table, $index, $fname = __METHOD__ ) { 1033 $table = $this->tableName( $table ); 1034 $table = strtoupper( $this->removeIdentifierQuotes( $table ) ); 1035 $index = strtoupper( $index ); 1036 $owner = strtoupper( $this->mDBname ); 1037 $sql = "SELECT 1 FROM all_indexes WHERE owner='$owner' AND index_name='{$table}_{$index}'"; 1038 $res = $this->doQuery( $sql ); 1039 if ( $res ) { 1040 $count = $res->numRows(); 1041 $res->free(); 1042 } else { 1043 $count = 0; 1044 } 1045 1046 return $count != 0; 1047 } 1048 1049 /** 1050 * Query whether a given table exists (in the given schema, or the default mw one if not given) 1051 * @param string $table 1052 * @param string $fname 1053 * @return bool 1054 */ 1055 function tableExists( $table, $fname = __METHOD__ ) { 1056 $table = $this->tableName( $table ); 1057 $table = $this->addQuotes( strtoupper( $this->removeIdentifierQuotes( $table ) ) ); 1058 $owner = $this->addQuotes( strtoupper( $this->mDBname ) ); 1059 $sql = "SELECT 1 FROM all_tables WHERE owner=$owner AND table_name=$table"; 1060 $res = $this->doQuery( $sql ); 1061 if ( $res && $res->numRows() > 0 ) { 1062 $exists = true; 1063 } else { 1064 $exists = false; 1065 } 1066 1067 $res->free(); 1068 1069 return $exists; 1070 } 1071 1072 /** 1073 * Function translates mysql_fetch_field() functionality on ORACLE. 1074 * Caching is present for reducing query time. 1075 * For internal calls. Use fieldInfo for normal usage. 1076 * Returns false if the field doesn't exist 1077 * 1078 * @param array|string $table 1079 * @param string $field 1080 * @return ORAField|ORAResult 1081 */ 1082 private function fieldInfoMulti( $table, $field ) { 1083 $field = strtoupper( $field ); 1084 if ( is_array( $table ) ) { 1085 $table = array_map( array( &$this, 'tableNameInternal' ), $table ); 1086 $tableWhere = 'IN ('; 1087 foreach ( $table as &$singleTable ) { 1088 $singleTable = $this->removeIdentifierQuotes( $singleTable ); 1089 if ( isset( $this->mFieldInfoCache["$singleTable.$field"] ) ) { 1090 return $this->mFieldInfoCache["$singleTable.$field"]; 1091 } 1092 $tableWhere .= '\'' . $singleTable . '\','; 1093 } 1094 $tableWhere = rtrim( $tableWhere, ',' ) . ')'; 1095 } else { 1096 $table = $this->removeIdentifierQuotes( $this->tableNameInternal( $table ) ); 1097 if ( isset( $this->mFieldInfoCache["$table.$field"] ) ) { 1098 return $this->mFieldInfoCache["$table.$field"]; 1099 } 1100 $tableWhere = '= \'' . $table . '\''; 1101 } 1102 1103 $fieldInfoStmt = oci_parse( 1104 $this->mConn, 1105 'SELECT * FROM wiki_field_info_full WHERE table_name ' . 1106 $tableWhere . ' and column_name = \'' . $field . '\'' 1107 ); 1108 if ( oci_execute( $fieldInfoStmt, $this->execFlags() ) === false ) { 1109 $e = oci_error( $fieldInfoStmt ); 1110 $this->reportQueryError( $e['message'], $e['code'], 'fieldInfo QUERY', __METHOD__ ); 1111 1112 return false; 1113 } 1114 $res = new ORAResult( $this, $fieldInfoStmt ); 1115 if ( $res->numRows() == 0 ) { 1116 if ( is_array( $table ) ) { 1117 foreach ( $table as &$singleTable ) { 1118 $this->mFieldInfoCache["$singleTable.$field"] = false; 1119 } 1120 } else { 1121 $this->mFieldInfoCache["$table.$field"] = false; 1122 } 1123 $fieldInfoTemp = null; 1124 } else { 1125 $fieldInfoTemp = new ORAField( $res->fetchRow() ); 1126 $table = $fieldInfoTemp->tableName(); 1127 $this->mFieldInfoCache["$table.$field"] = $fieldInfoTemp; 1128 } 1129 $res->free(); 1130 1131 return $fieldInfoTemp; 1132 } 1133 1134 /** 1135 * @throws DBUnexpectedError 1136 * @param string $table 1137 * @param string $field 1138 * @return ORAField 1139 */ 1140 function fieldInfo( $table, $field ) { 1141 if ( is_array( $table ) ) { 1142 throw new DBUnexpectedError( $this, 'DatabaseOracle::fieldInfo called with table array!' ); 1143 } 1144 1145 return $this->fieldInfoMulti( $table, $field ); 1146 } 1147 1148 protected function doBegin( $fname = __METHOD__ ) { 1149 $this->mTrxLevel = 1; 1150 $this->doQuery( 'SET CONSTRAINTS ALL DEFERRED' ); 1151 } 1152 1153 protected function doCommit( $fname = __METHOD__ ) { 1154 if ( $this->mTrxLevel ) { 1155 $ret = oci_commit( $this->mConn ); 1156 if ( !$ret ) { 1157 throw new DBUnexpectedError( $this, $this->lastError() ); 1158 } 1159 $this->mTrxLevel = 0; 1160 $this->doQuery( 'SET CONSTRAINTS ALL IMMEDIATE' ); 1161 } 1162 } 1163 1164 protected function doRollback( $fname = __METHOD__ ) { 1165 if ( $this->mTrxLevel ) { 1166 oci_rollback( $this->mConn ); 1167 $this->mTrxLevel = 0; 1168 $this->doQuery( 'SET CONSTRAINTS ALL IMMEDIATE' ); 1169 } 1170 } 1171 1172 /** 1173 * defines must comply with ^define\s*([^\s=]*)\s*=\s?'\{\$([^\}]*)\}'; 1174 * 1175 * @param resource $fp 1176 * @param bool|string $lineCallback 1177 * @param bool|callable $resultCallback 1178 * @param string $fname 1179 * @param bool|callable $inputCallback 1180 * @return bool|string 1181 */ 1182 function sourceStream( $fp, $lineCallback = false, $resultCallback = false, 1183 $fname = __METHOD__, $inputCallback = false ) { 1184 $cmd = ''; 1185 $done = false; 1186 $dollarquote = false; 1187 1188 $replacements = array(); 1189 1190 while ( !feof( $fp ) ) { 1191 if ( $lineCallback ) { 1192 call_user_func( $lineCallback ); 1193 } 1194 $line = trim( fgets( $fp, 1024 ) ); 1195 $sl = strlen( $line ) - 1; 1196 1197 if ( $sl < 0 ) { 1198 continue; 1199 } 1200 if ( '-' == $line[0] && '-' == $line[1] ) { 1201 continue; 1202 } 1203 1204 // Allow dollar quoting for function declarations 1205 if ( substr( $line, 0, 8 ) == '/*$mw$*/' ) { 1206 if ( $dollarquote ) { 1207 $dollarquote = false; 1208 $line = str_replace( '/*$mw$*/', '', $line ); // remove dollarquotes 1209 $done = true; 1210 } else { 1211 $dollarquote = true; 1212 } 1213 } elseif ( !$dollarquote ) { 1214 if ( ';' == $line[$sl] && ( $sl < 2 || ';' != $line[$sl - 1] ) ) { 1215 $done = true; 1216 $line = substr( $line, 0, $sl ); 1217 } 1218 } 1219 1220 if ( $cmd != '' ) { 1221 $cmd .= ' '; 1222 } 1223 $cmd .= "$line\n"; 1224 1225 if ( $done ) { 1226 $cmd = str_replace( ';;', ";", $cmd ); 1227 if ( strtolower( substr( $cmd, 0, 6 ) ) == 'define' ) { 1228 if ( preg_match( '/^define\s*([^\s=]*)\s*=\s*\'\{\$([^\}]*)\}\'/', $cmd, $defines ) ) { 1229 $replacements[$defines[2]] = $defines[1]; 1230 } 1231 } else { 1232 foreach ( $replacements as $mwVar => $scVar ) { 1233 $cmd = str_replace( '&' . $scVar . '.', '`{$' . $mwVar . '}`', $cmd ); 1234 } 1235 1236 $cmd = $this->replaceVars( $cmd ); 1237 if ( $inputCallback ) { 1238 call_user_func( $inputCallback, $cmd ); 1239 } 1240 $res = $this->doQuery( $cmd ); 1241 if ( $resultCallback ) { 1242 call_user_func( $resultCallback, $res, $this ); 1243 } 1244 1245 if ( false === $res ) { 1246 $err = $this->lastError(); 1247 1248 return "Query \"{$cmd}\" failed with error code \"$err\".\n"; 1249 } 1250 } 1251 1252 $cmd = ''; 1253 $done = false; 1254 } 1255 } 1256 1257 return true; 1258 } 1259 1260 function selectDB( $db ) { 1261 $this->mDBname = $db; 1262 if ( $db == null || $db == $this->mUser ) { 1263 return true; 1264 } 1265 $sql = 'ALTER SESSION SET CURRENT_SCHEMA=' . strtoupper( $db ); 1266 $stmt = oci_parse( $this->mConn, $sql ); 1267 wfSuppressWarnings(); 1268 $success = oci_execute( $stmt ); 1269 wfRestoreWarnings(); 1270 if ( !$success ) { 1271 $e = oci_error( $stmt ); 1272 if ( $e['code'] != '1435' ) { 1273 $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ ); 1274 } 1275 1276 return false; 1277 } 1278 1279 return true; 1280 } 1281 1282 function strencode( $s ) { 1283 return str_replace( "'", "''", $s ); 1284 } 1285 1286 function addQuotes( $s ) { 1287 global $wgContLang; 1288 if ( isset( $wgContLang->mLoaded ) && $wgContLang->mLoaded ) { 1289 $s = $wgContLang->checkTitleEncoding( $s ); 1290 } 1291 1292 return "'" . $this->strencode( $s ) . "'"; 1293 } 1294 1295 public function addIdentifierQuotes( $s ) { 1296 if ( !$this->getFlag( DBO_DDLMODE ) ) { 1297 $s = '/*Q*/' . $s; 1298 } 1299 1300 return $s; 1301 } 1302 1303 public function removeIdentifierQuotes( $s ) { 1304 return strpos( $s, '/*Q*/' ) === false ? $s : substr( $s, 5 ); 1305 } 1306 1307 public function isQuotedIdentifier( $s ) { 1308 return strpos( $s, '/*Q*/' ) !== false; 1309 } 1310 1311 private function wrapFieldForWhere( $table, &$col, &$val ) { 1312 global $wgContLang; 1313 1314 $col_info = $this->fieldInfoMulti( $table, $col ); 1315 $col_type = $col_info != false ? $col_info->type() : 'CONSTANT'; 1316 if ( $col_type == 'CLOB' ) { 1317 $col = 'TO_CHAR(' . $col . ')'; 1318 $val = $wgContLang->checkTitleEncoding( $val ); 1319 } elseif ( $col_type == 'VARCHAR2' ) { 1320 $val = $wgContLang->checkTitleEncoding( $val ); 1321 } 1322 } 1323 1324 private function wrapConditionsForWhere( $table, $conds, $parentCol = null ) { 1325 $conds2 = array(); 1326 foreach ( $conds as $col => $val ) { 1327 if ( is_array( $val ) ) { 1328 $conds2[$col] = $this->wrapConditionsForWhere( $table, $val, $col ); 1329 } else { 1330 if ( is_numeric( $col ) && $parentCol != null ) { 1331 $this->wrapFieldForWhere( $table, $parentCol, $val ); 1332 } else { 1333 $this->wrapFieldForWhere( $table, $col, $val ); 1334 } 1335 $conds2[$col] = $val; 1336 } 1337 } 1338 1339 return $conds2; 1340 } 1341 1342 function selectRow( $table, $vars, $conds, $fname = __METHOD__, 1343 $options = array(), $join_conds = array() 1344 ) { 1345 if ( is_array( $conds ) ) { 1346 $conds = $this->wrapConditionsForWhere( $table, $conds ); 1347 } 1348 1349 return parent::selectRow( $table, $vars, $conds, $fname, $options, $join_conds ); 1350 } 1351 1352 /** 1353 * Returns an optional USE INDEX clause to go after the table, and a 1354 * string to go at the end of the query 1355 * 1356 * @param array $options An associative array of options to be turned into 1357 * an SQL query, valid keys are listed in the function. 1358 * @return array 1359 */ 1360 function makeSelectOptions( $options ) { 1361 $preLimitTail = $postLimitTail = ''; 1362 $startOpts = ''; 1363 1364 $noKeyOptions = array(); 1365 foreach ( $options as $key => $option ) { 1366 if ( is_numeric( $key ) ) { 1367 $noKeyOptions[$option] = true; 1368 } 1369 } 1370 1371 $preLimitTail .= $this->makeGroupByWithHaving( $options ); 1372 1373 $preLimitTail .= $this->makeOrderBy( $options ); 1374 1375 if ( isset( $noKeyOptions['FOR UPDATE'] ) ) { 1376 $postLimitTail .= ' FOR UPDATE'; 1377 } 1378 1379 if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) { 1380 $startOpts .= 'DISTINCT'; 1381 } 1382 1383 if ( isset( $options['USE INDEX'] ) && !is_array( $options['USE INDEX'] ) ) { 1384 $useIndex = $this->useIndexClause( $options['USE INDEX'] ); 1385 } else { 1386 $useIndex = ''; 1387 } 1388 1389 return array( $startOpts, $useIndex, $preLimitTail, $postLimitTail ); 1390 } 1391 1392 public function delete( $table, $conds, $fname = __METHOD__ ) { 1393 if ( is_array( $conds ) ) { 1394 $conds = $this->wrapConditionsForWhere( $table, $conds ); 1395 } 1396 // a hack for deleting pages, users and images (which have non-nullable FKs) 1397 // all deletions on these tables have transactions so final failure rollbacks these updates 1398 $table = $this->tableName( $table ); 1399 if ( $table == $this->tableName( 'user' ) ) { 1400 $this->update( 'archive', array( 'ar_user' => 0 ), 1401 array( 'ar_user' => $conds['user_id'] ), $fname ); 1402 $this->update( 'ipblocks', array( 'ipb_user' => 0 ), 1403 array( 'ipb_user' => $conds['user_id'] ), $fname ); 1404 $this->update( 'image', array( 'img_user' => 0 ), 1405 array( 'img_user' => $conds['user_id'] ), $fname ); 1406 $this->update( 'oldimage', array( 'oi_user' => 0 ), 1407 array( 'oi_user' => $conds['user_id'] ), $fname ); 1408 $this->update( 'filearchive', array( 'fa_deleted_user' => 0 ), 1409 array( 'fa_deleted_user' => $conds['user_id'] ), $fname ); 1410 $this->update( 'filearchive', array( 'fa_user' => 0 ), 1411 array( 'fa_user' => $conds['user_id'] ), $fname ); 1412 $this->update( 'uploadstash', array( 'us_user' => 0 ), 1413 array( 'us_user' => $conds['user_id'] ), $fname ); 1414 $this->update( 'recentchanges', array( 'rc_user' => 0 ), 1415 array( 'rc_user' => $conds['user_id'] ), $fname ); 1416 $this->update( 'logging', array( 'log_user' => 0 ), 1417 array( 'log_user' => $conds['user_id'] ), $fname ); 1418 } elseif ( $table == $this->tableName( 'image' ) ) { 1419 $this->update( 'oldimage', array( 'oi_name' => 0 ), 1420 array( 'oi_name' => $conds['img_name'] ), $fname ); 1421 } 1422 1423 return parent::delete( $table, $conds, $fname ); 1424 } 1425 1426 /** 1427 * @param string $table 1428 * @param array $values 1429 * @param array $conds 1430 * @param string $fname 1431 * @param array $options 1432 * @return bool 1433 * @throws DBUnexpectedError 1434 */ 1435 function update( $table, $values, $conds, $fname = __METHOD__, $options = array() ) { 1436 global $wgContLang; 1437 1438 $table = $this->tableName( $table ); 1439 $opts = $this->makeUpdateOptions( $options ); 1440 $sql = "UPDATE $opts $table SET "; 1441 1442 $first = true; 1443 foreach ( $values as $col => &$val ) { 1444 $sqlSet = $this->fieldBindStatement( $table, $col, $val, true ); 1445 1446 if ( !$first ) { 1447 $sqlSet = ', ' . $sqlSet; 1448 } else { 1449 $first = false; 1450 } 1451 $sql .= $sqlSet; 1452 } 1453 1454 if ( $conds !== array() && $conds !== '*' ) { 1455 $conds = $this->wrapConditionsForWhere( $table, $conds ); 1456 $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND ); 1457 } 1458 1459 if ( ( $this->mLastResult = $stmt = oci_parse( $this->mConn, $sql ) ) === false ) { 1460 $e = oci_error( $this->mConn ); 1461 $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ ); 1462 1463 return false; 1464 } 1465 foreach ( $values as $col => &$val ) { 1466 $col_info = $this->fieldInfoMulti( $table, $col ); 1467 $col_type = $col_info != false ? $col_info->type() : 'CONSTANT'; 1468 1469 if ( $val === null ) { 1470 // do nothing ... null was inserted in statement creation 1471 } elseif ( $col_type != 'BLOB' && $col_type != 'CLOB' ) { 1472 if ( is_object( $val ) ) { 1473 $val = $val->getData(); 1474 } 1475 1476 if ( preg_match( '/^timestamp.*/i', $col_type ) == 1 && strtolower( $val ) == 'infinity' ) { 1477 $val = '31-12-2030 12:00:00.000000'; 1478 } 1479 1480 $val = ( $wgContLang != null ) ? $wgContLang->checkTitleEncoding( $val ) : $val; 1481 if ( oci_bind_by_name( $stmt, ":$col", $val ) === false ) { 1482 $e = oci_error( $stmt ); 1483 $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ ); 1484 1485 return false; 1486 } 1487 } else { 1488 /** @var OCI_Lob[] $lob */ 1489 if ( ( $lob[$col] = oci_new_descriptor( $this->mConn, OCI_D_LOB ) ) === false ) { 1490 $e = oci_error( $stmt ); 1491 throw new DBUnexpectedError( $this, "Cannot create LOB descriptor: " . $e['message'] ); 1492 } 1493 1494 if ( is_object( $val ) ) { 1495 $val = $val->getData(); 1496 } 1497 1498 if ( $col_type == 'BLOB' ) { 1499 $lob[$col]->writeTemporary( $val ); 1500 oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, SQLT_BLOB ); 1501 } else { 1502 $lob[$col]->writeTemporary( $val ); 1503 oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, OCI_B_CLOB ); 1504 } 1505 } 1506 } 1507 1508 wfSuppressWarnings(); 1509 1510 if ( oci_execute( $stmt, $this->execFlags() ) === false ) { 1511 $e = oci_error( $stmt ); 1512 if ( !$this->ignoreDupValOnIndex || $e['code'] != '1' ) { 1513 $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ ); 1514 1515 return false; 1516 } else { 1517 $this->mAffectedRows = oci_num_rows( $stmt ); 1518 } 1519 } else { 1520 $this->mAffectedRows = oci_num_rows( $stmt ); 1521 } 1522 1523 wfRestoreWarnings(); 1524 1525 if ( isset( $lob ) ) { 1526 foreach ( $lob as $lob_v ) { 1527 $lob_v->free(); 1528 } 1529 } 1530 1531 if ( !$this->mTrxLevel ) { 1532 oci_commit( $this->mConn ); 1533 } 1534 1535 return oci_free_statement( $stmt ); 1536 } 1537 1538 function bitNot( $field ) { 1539 // expecting bit-fields smaller than 4bytes 1540 return 'BITNOT(' . $field . ')'; 1541 } 1542 1543 function bitAnd( $fieldLeft, $fieldRight ) { 1544 return 'BITAND(' . $fieldLeft . ', ' . $fieldRight . ')'; 1545 } 1546 1547 function bitOr( $fieldLeft, $fieldRight ) { 1548 return 'BITOR(' . $fieldLeft . ', ' . $fieldRight . ')'; 1549 } 1550 1551 function getDBname() { 1552 return $this->mDBname; 1553 } 1554 1555 function getServer() { 1556 return $this->mServer; 1557 } 1558 1559 public function buildGroupConcatField( 1560 $delim, $table, $field, $conds = '', $join_conds = array() 1561 ) { 1562 $fld = "LISTAGG($field," . $this->addQuotes( $delim ) . ") WITHIN GROUP (ORDER BY $field)"; 1563 1564 return '(' . $this->selectSQLText( $table, $fld, $conds, null, array(), $join_conds ) . ')'; 1565 } 1566 1567 public function getSearchEngine() { 1568 return 'SearchOracle'; 1569 } 1570 1571 public function getInfinity() { 1572 return '31-12-2030 12:00:00.000000'; 1573 } 1574 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |