1<?php 2/* 3 4 version V4.65 22 July 2005 (c) 2000-2005 John Lim. All rights reserved. 5 6 Released under both BSD license and Lesser GPL library license. 7 Whenever there is any discrepancy between the two licenses, 8 the BSD license will take precedence. 9 10 Latest version is available at http://adodb.sourceforge.net 11 12 Code contributed by George Fourlanos <fou@infomap.gr> 13 14 13 Nov 2000 jlim - removed all ora_* references. 15*/ 16 17// security - hide paths 18if (!defined('ADODB_DIR')) die(); 19 20/* 21NLS_Date_Format 22Allows you to use a date format other than the Oracle Lite default. When a literal 23character string appears where a date value is expected, the Oracle Lite database 24tests the string to see if it matches the formats of Oracle, SQL-92, or the value 25specified for this parameter in the POLITE.INI file. Setting this parameter also 26defines the default format used in the TO_CHAR or TO_DATE functions when no 27other format string is supplied. 28 29For Oracle the default is dd-mon-yy or dd-mon-yyyy, and for SQL-92 the default is 30yy-mm-dd or yyyy-mm-dd. 31 32Using 'RR' in the format forces two-digit years less than or equal to 49 to be 33interpreted as years in the 21st century (2000�2049), and years over 50 as years in 34the 20th century (1950�1999). Setting the RR format as the default for all two-digit 35year entries allows you to become year-2000 compliant. For example: 36NLS_DATE_FORMAT='RR-MM-DD' 37 38You can also modify the date format using the ALTER SESSION command. 39*/ 40 41# define the LOB descriptor type for the given type 42# returns false if no LOB descriptor 43function oci_lob_desc($type) { 44 switch ($type) { 45 case OCI_B_BFILE: $result = OCI_D_FILE; break; 46 case OCI_B_CFILEE: $result = OCI_D_FILE; break; 47 case OCI_B_CLOB: $result = OCI_D_LOB; break; 48 case OCI_B_BLOB: $result = OCI_D_LOB; break; 49 case OCI_B_ROWID: $result = OCI_D_ROWID; break; 50 default: $result = false; break; 51 } 52 return $result; 53} 54 55class ADODB_oci8 extends ADOConnection { 56 var $databaseType = 'oci8'; 57 var $dataProvider = 'oci8'; 58 var $replaceQuote = "''"; // string to use to replace quotes 59 var $concat_operator='||'; 60 var $sysDate = "TRUNC(SYSDATE)"; 61 var $sysTimeStamp = 'SYSDATE'; 62 var $metaDatabasesSQL = "SELECT USERNAME FROM ALL_USERS WHERE USERNAME NOT IN ('SYS','SYSTEM','DBSNMP','OUTLN') ORDER BY 1"; 63 var $_stmt; 64 var $_commit = OCI_COMMIT_ON_SUCCESS; 65 var $_initdate = true; // init date to YYYY-MM-DD 66 var $metaTablesSQL = "select table_name,table_type from cat where table_type in ('TABLE','VIEW')"; 67 var $metaColumnsSQL = "select cname,coltype,width, SCALE, PRECISION, NULLS, DEFAULTVAL from col where tname='%s' order by colno"; //changed by smondino@users.sourceforge. net 68 var $_bindInputArray = true; 69 var $hasGenID = true; 70 var $_genIDSQL = "SELECT (%s.nextval) FROM DUAL"; 71 var $_genSeqSQL = "CREATE SEQUENCE %s START WITH %s"; 72 var $_dropSeqSQL = "DROP SEQUENCE %s"; 73 var $hasAffectedRows = true; 74 var $random = "abs(mod(DBMS_RANDOM.RANDOM,10000001)/10000000)"; 75 var $noNullStrings = false; 76 var $connectSID = false; 77 var $_bind = false; 78 var $_hasOCIFetchStatement = false; 79 var $_getarray = false; // currently not working 80 var $leftOuter = ''; // oracle wierdness, $col = $value (+) for LEFT OUTER, $col (+)= $value for RIGHT OUTER 81 var $session_sharing_force_blob = false; // alter session on updateblob if set to true 82 var $firstrows = true; // enable first rows optimization on SelectLimit() 83 var $selectOffsetAlg1 = 100; // when to use 1st algorithm of selectlimit. 84 var $NLS_DATE_FORMAT = 'YYYY-MM-DD'; // To include time, use 'RRRR-MM-DD HH24:MI:SS' 85 var $useDBDateFormatForTextInput=false; 86 var $datetime = false; // MetaType('DATE') returns 'D' (datetime==false) or 'T' (datetime == true) 87 var $_refLOBs = array(); 88 89 // var $ansiOuter = true; // if oracle9 90 91 function ADODB_oci8() 92 { 93 $this->_hasOCIFetchStatement = ADODB_PHPVER >= 0x4200; 94 if (defined('ADODB_EXTENSION')) $this->rsPrefix .= 'ext_'; 95 } 96 97 /* Function &MetaColumns($table) added by smondino@users.sourceforge.net*/ 98 function &MetaColumns($table) 99 { 100 global $ADODB_FETCH_MODE; 101 102 $false = false; 103 $save = $ADODB_FETCH_MODE; 104 $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 105 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); 106 107 $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table))); 108 109 if (isset($savem)) $this->SetFetchMode($savem); 110 $ADODB_FETCH_MODE = $save; 111 if (!$rs) { 112 return $false; 113 } 114 $retarr = array(); 115 while (!$rs->EOF) { //print_r($rs->fields); 116 $fld = new ADOFieldObject(); 117 $fld->name = $rs->fields[0]; 118 $fld->type = $rs->fields[1]; 119 $fld->max_length = $rs->fields[2]; 120 $fld->scale = $rs->fields[3]; 121 if ($rs->fields[1] == 'NUMBER' && $rs->fields[3] == 0) { 122 $fld->type ='INT'; 123 $fld->max_length = $rs->fields[4]; 124 } 125 $fld->not_null = (strncmp($rs->fields[5], 'NOT',3) === 0); 126 $fld->binary = (strpos($fld->type,'BLOB') !== false); 127 $fld->default_value = $rs->fields[6]; 128 129 if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld; 130 else $retarr[strtoupper($fld->name)] = $fld; 131 $rs->MoveNext(); 132 } 133 $rs->Close(); 134 if (empty($retarr)) 135 return $false; 136 else 137 return $retarr; 138 } 139 140 function Time() 141 { 142 $rs =& $this->Execute("select TO_CHAR($this->sysTimeStamp,'YYYY-MM-DD HH24:MI:SS') from dual"); 143 if ($rs && !$rs->EOF) return $this->UnixTimeStamp(reset($rs->fields)); 144 145 return false; 146 } 147 148/* 149 150 Multiple modes of connection are supported: 151 152 a. Local Database 153 $conn->Connect(false,'scott','tiger'); 154 155 b. From tnsnames.ora 156 $conn->Connect(false,'scott','tiger',$tnsname); 157 $conn->Connect($tnsname,'scott','tiger'); 158 159 c. Server + service name 160 $conn->Connect($serveraddress,'scott,'tiger',$service_name); 161 162 d. Server + SID 163 $conn->connectSID = true; 164 $conn->Connect($serveraddress,'scott,'tiger',$SID); 165 166 167Example TNSName: 168--------------- 169NATSOFT.DOMAIN = 170 (DESCRIPTION = 171 (ADDRESS_LIST = 172 (ADDRESS = (PROTOCOL = TCP)(HOST = kermit)(PORT = 1523)) 173 ) 174 (CONNECT_DATA = 175 (SERVICE_NAME = natsoft.domain) 176 ) 177 ) 178 179 There are 3 connection modes, 0 = non-persistent, 1 = persistent, 2 = force new connection 180 181*/ 182 function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$mode=0) 183 { 184 if (!function_exists('OCIPLogon')) return null; 185 186 187 $this->_errorMsg = false; 188 $this->_errorCode = false; 189 190 if($argHostname) { // added by Jorma Tuomainen <jorma.tuomainen@ppoy.fi> 191 if (empty($argDatabasename)) $argDatabasename = $argHostname; 192 else { 193 if(strpos($argHostname,":")) { 194 $argHostinfo=explode(":",$argHostname); 195 $argHostname=$argHostinfo[0]; 196 $argHostport=$argHostinfo[1]; 197 } else { 198 $argHostport="1521"; 199 } 200 201 if ($this->connectSID) { 202 $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname 203 .")(PORT=$argHostport))(CONNECT_DATA=(SID=$argDatabasename)))"; 204 } else 205 $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname 206 .")(PORT=$argHostport))(CONNECT_DATA=(SERVICE_NAME=$argDatabasename)))"; 207 } 208 } 209 210 //if ($argHostname) print "<p>Connect: 1st argument should be left blank for $this->databaseType</p>"; 211 if ($mode==1) { 212 $this->_connectionID = ($this->charSet) ? 213 OCIPLogon($argUsername,$argPassword, $argDatabasename) 214 : 215 OCIPLogon($argUsername,$argPassword, $argDatabasename, $this->charSet) 216 ; 217 if ($this->_connectionID && $this->autoRollback) OCIrollback($this->_connectionID); 218 } else if ($mode==2) { 219 $this->_connectionID = ($this->charSet) ? 220 OCINLogon($argUsername,$argPassword, $argDatabasename) 221 : 222 OCINLogon($argUsername,$argPassword, $argDatabasename, $this->charSet); 223 224 } else { 225 $this->_connectionID = ($this->charSet) ? 226 OCILogon($argUsername,$argPassword, $argDatabasename) 227 : 228 OCILogon($argUsername,$argPassword, $argDatabasename,$this->charSet); 229 } 230 if (!$this->_connectionID) return false; 231 if ($this->_initdate) { 232 $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='".$this->NLS_DATE_FORMAT."'"); 233 } 234 235 // looks like: 236 // Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production With the Partitioning option JServer Release 8.1.7.0.0 - Production 237 // $vers = OCIServerVersion($this->_connectionID); 238 // if (strpos($vers,'8i') !== false) $this->ansiOuter = true; 239 return true; 240 } 241 242 function ServerInfo() 243 { 244 $arr['compat'] = $this->GetOne('select value from sys.database_compatible_level'); 245 $arr['description'] = @OCIServerVersion($this->_connectionID); 246 $arr['version'] = ADOConnection::_findvers($arr['description']); 247 return $arr; 248 } 249 // returns true or false 250 function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) 251 { 252 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,1); 253 } 254 255 // returns true or false 256 function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename) 257 { 258 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,2); 259 } 260 261 function _affectedrows() 262 { 263 if (is_resource($this->_stmt)) return @OCIRowCount($this->_stmt); 264 return 0; 265 } 266 267 function IfNull( $field, $ifNull ) 268 { 269 return " NVL($field, $ifNull) "; // if Oracle 270 } 271 272 // format and return date string in database date format 273 function DBDate($d) 274 { 275 if (empty($d) && $d !== 0) return 'null'; 276 277 if (is_string($d)) $d = ADORecordSet::UnixDate($d); 278 return "TO_DATE(".adodb_date($this->fmtDate,$d).",'".$this->NLS_DATE_FORMAT."')"; 279 } 280 281 282 // format and return date string in database timestamp format 283 function DBTimeStamp($ts) 284 { 285 if (empty($ts) && $ts !== 0) return 'null'; 286 if (is_string($ts)) $ts = ADORecordSet::UnixTimeStamp($ts); 287 return 'TO_DATE('.adodb_date($this->fmtTimeStamp,$ts).",'RRRR-MM-DD, HH:MI:SS AM')"; 288 } 289 290 function RowLock($tables,$where,$flds='1 as ignore') 291 { 292 if ($this->autoCommit) $this->BeginTrans(); 293 return $this->GetOne("select $flds from $tables where $where for update"); 294 } 295 296 function &MetaTables($ttype=false,$showSchema=false,$mask=false) 297 { 298 if ($mask) { 299 $save = $this->metaTablesSQL; 300 $mask = $this->qstr(strtoupper($mask)); 301 $this->metaTablesSQL .= " AND table_name like $mask"; 302 } 303 $ret =& ADOConnection::MetaTables($ttype,$showSchema); 304 305 if ($mask) { 306 $this->metaTablesSQL = $save; 307 } 308 return $ret; 309 } 310 311 // Mark Newnham 312 function &MetaIndexes ($table, $primary = FALSE, $owner=false) 313 { 314 // save old fetch mode 315 global $ADODB_FETCH_MODE; 316 317 $save = $ADODB_FETCH_MODE; 318 $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 319 320 if ($this->fetchMode !== FALSE) { 321 $savem = $this->SetFetchMode(FALSE); 322 } 323 324 // get index details 325 $table = strtoupper($table); 326 327 // get Primary index 328 $primary_key = ''; 329 330 $false = false; 331 $rs = $this->Execute(sprintf("SELECT * FROM ALL_CONSTRAINTS WHERE UPPER(TABLE_NAME)='%s' AND CONSTRAINT_TYPE='P'",$table)); 332 if ($row = $rs->FetchRow()) 333 $primary_key = $row[1]; //constraint_name 334 335 if ($primary==TRUE && $primary_key=='') { 336 if (isset($savem)) 337 $this->SetFetchMode($savem); 338 $ADODB_FETCH_MODE = $save; 339 return $false; //There is no primary key 340 } 341 342 $rs = $this->Execute(sprintf("SELECT ALL_INDEXES.INDEX_NAME, ALL_INDEXES.UNIQUENESS, ALL_IND_COLUMNS.COLUMN_POSITION, ALL_IND_COLUMNS.COLUMN_NAME FROM ALL_INDEXES,ALL_IND_COLUMNS WHERE UPPER(ALL_INDEXES.TABLE_NAME)='%s' AND ALL_IND_COLUMNS.INDEX_NAME=ALL_INDEXES.INDEX_NAME",$table)); 343 344 345 if (!is_object($rs)) { 346 if (isset($savem)) 347 $this->SetFetchMode($savem); 348 $ADODB_FETCH_MODE = $save; 349 return $false; 350 } 351 352 $indexes = array (); 353 // parse index data into array 354 355 while ($row = $rs->FetchRow()) { 356 if ($primary && $row[0] != $primary_key) continue; 357 if (!isset($indexes[$row[0]])) { 358 $indexes[$row[0]] = array( 359 'unique' => ($row[1] == 'UNIQUE'), 360 'columns' => array() 361 ); 362 } 363 $indexes[$row[0]]['columns'][$row[2] - 1] = $row[3]; 364 } 365 366 // sort columns by order in the index 367 foreach ( array_keys ($indexes) as $index ) { 368 ksort ($indexes[$index]['columns']); 369 } 370 371 if (isset($savem)) { 372 $this->SetFetchMode($savem); 373 $ADODB_FETCH_MODE = $save; 374 } 375 return $indexes; 376 } 377 378 function BeginTrans() 379 { 380 if ($this->transOff) return true; 381 $this->transCnt += 1; 382 $this->autoCommit = false; 383 $this->_commit = OCI_DEFAULT; 384 return true; 385 } 386 387 function CommitTrans($ok=true) 388 { 389 if ($this->transOff) return true; 390 if (!$ok) return $this->RollbackTrans(); 391 392 if ($this->transCnt) $this->transCnt -= 1; 393 $ret = OCIcommit($this->_connectionID); 394 $this->_commit = OCI_COMMIT_ON_SUCCESS; 395 $this->autoCommit = true; 396 return $ret; 397 } 398 399 function RollbackTrans() 400 { 401 if ($this->transOff) return true; 402 if ($this->transCnt) $this->transCnt -= 1; 403 $ret = OCIrollback($this->_connectionID); 404 $this->_commit = OCI_COMMIT_ON_SUCCESS; 405 $this->autoCommit = true; 406 return $ret; 407 } 408 409 410 function SelectDB($dbName) 411 { 412 return false; 413 } 414 415 function ErrorMsg() 416 { 417 if ($this->_errorMsg !== false) return $this->_errorMsg; 418 419 if (is_resource($this->_stmt)) $arr = @OCIerror($this->_stmt); 420 if (empty($arr)) { 421 $arr = @OCIerror($this->_connectionID); 422 if ($arr === false) $arr = @OCIError(); 423 if ($arr === false) return ''; 424 } 425 $this->_errorMsg = $arr['message']; 426 $this->_errorCode = $arr['code']; 427 return $this->_errorMsg; 428 } 429 430 function ErrorNo() 431 { 432 if ($this->_errorCode !== false) return $this->_errorCode; 433 434 if (is_resource($this->_stmt)) $arr = @OCIError($this->_stmt); 435 if (empty($arr)) { 436 $arr = @OCIError($this->_connectionID); 437 if ($arr == false) $arr = @OCIError(); 438 if ($arr == false) return ''; 439 } 440 441 $this->_errorMsg = $arr['message']; 442 $this->_errorCode = $arr['code']; 443 444 return $arr['code']; 445 } 446 447 // Format date column in sql string given an input format that understands Y M D 448 function SQLDate($fmt, $col=false) 449 { 450 if (!$col) $col = $this->sysTimeStamp; 451 $s = 'TO_CHAR('.$col.",'"; 452 453 $len = strlen($fmt); 454 for ($i=0; $i < $len; $i++) { 455 $ch = $fmt[$i]; 456 switch($ch) { 457 case 'Y': 458 case 'y': 459 $s .= 'YYYY'; 460 break; 461 case 'Q': 462 case 'q': 463 $s .= 'Q'; 464 break; 465 466 case 'M': 467 $s .= 'Mon'; 468 break; 469 470 case 'm': 471 $s .= 'MM'; 472 break; 473 case 'D': 474 case 'd': 475 $s .= 'DD'; 476 break; 477 478 case 'H': 479 $s.= 'HH24'; 480 break; 481 482 case 'h': 483 $s .= 'HH'; 484 break; 485 486 case 'i': 487 $s .= 'MI'; 488 break; 489 490 case 's': 491 $s .= 'SS'; 492 break; 493 494 case 'a': 495 case 'A': 496 $s .= 'AM'; 497 break; 498 499 case 'w': 500 $s .= 'D'; 501 break; 502 503 case 'l': 504 $s .= 'DAY'; 505 break; 506 507 default: 508 // handle escape characters... 509 if ($ch == '\\') { 510 $i++; 511 $ch = substr($fmt,$i,1); 512 } 513 if (strpos('-/.:;, ',$ch) !== false) $s .= $ch; 514 else $s .= '"'.$ch.'"'; 515 516 } 517 } 518 return $s. "')"; 519 } 520 521 522 /* 523 This algorithm makes use of 524 525 a. FIRST_ROWS hint 526 The FIRST_ROWS hint explicitly chooses the approach to optimize response time, 527 that is, minimum resource usage to return the first row. Results will be returned 528 as soon as they are identified. 529 530 b. Uses rownum tricks to obtain only the required rows from a given offset. 531 As this uses complicated sql statements, we only use this if the $offset >= 100. 532 This idea by Tomas V V Cox. 533 534 This implementation does not appear to work with oracle 8.0.5 or earlier. Comment 535 out this function then, and the slower SelectLimit() in the base class will be used. 536 */ 537 function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0) 538 { 539 // seems that oracle only supports 1 hint comment in 8i 540 if ($this->firstrows) { 541 if (strpos($sql,'/*+') !== false) 542 $sql = str_replace('/*+ ','/*+FIRST_ROWS ',$sql); 543 else 544 $sql = preg_replace('/^[ \t\n]*select/i','SELECT /*+FIRST_ROWS*/',$sql); 545 } 546 547 if ($offset < $this->selectOffsetAlg1) { 548 if ($nrows > 0) { 549 if ($offset > 0) $nrows += $offset; 550 //$inputarr['adodb_rownum'] = $nrows; 551 if ($this->databaseType == 'oci8po') { 552 $sql = "select * from (".$sql.") where rownum <= ?"; 553 } else { 554 $sql = "select * from (".$sql.") where rownum <= :adodb_offset"; 555 } 556 $inputarr['adodb_offset'] = $nrows; 557 $nrows = -1; 558 } 559 // note that $nrows = 0 still has to work ==> no rows returned 560 561 $rs =& ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache); 562 return $rs; 563 564 } else { 565 // Algorithm by Tomas V V Cox, from PEAR DB oci8.php 566 567 // Let Oracle return the name of the columns 568 $q_fields = "SELECT * FROM (".$sql.") WHERE NULL = NULL"; 569 570 $false = false; 571 if (! $stmt_arr = $this->Prepare($q_fields)) { 572 return $false; 573 } 574 $stmt = $stmt_arr[1]; 575 576 if (is_array($inputarr)) { 577 foreach($inputarr as $k => $v) { 578 if (is_array($v)) { 579 if (sizeof($v) == 2) // suggested by g.giunta@libero. 580 OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]); 581 else 582 OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]); 583 } else { 584 $len = -1; 585 if ($v === ' ') $len = 1; 586 if (isset($bindarr)) { // is prepared sql, so no need to ocibindbyname again 587 $bindarr[$k] = $v; 588 } else { // dynamic sql, so rebind every time 589 OCIBindByName($stmt,":$k",$inputarr[$k],$len); 590 } 591 } 592 } 593 } 594 595 if (!OCIExecute($stmt, OCI_DEFAULT)) { 596 OCIFreeStatement($stmt); 597 return $false; 598 } 599 600 $ncols = OCINumCols($stmt); 601 for ( $i = 1; $i <= $ncols; $i++ ) { 602 $cols[] = '"'.OCIColumnName($stmt, $i).'"'; 603 } 604 $result = false; 605 606 OCIFreeStatement($stmt); 607 $fields = implode(',', $cols); 608 $nrows += $offset; 609 $offset += 1; // in Oracle rownum starts at 1 610 611 if ($this->databaseType == 'oci8po') { 612 $sql = "SELECT $fields FROM". 613 "(SELECT rownum as adodb_rownum, $fields FROM". 614 " ($sql) WHERE rownum <= ?". 615 ") WHERE adodb_rownum >= ?"; 616 } else { 617 $sql = "SELECT $fields FROM". 618 "(SELECT rownum as adodb_rownum, $fields FROM". 619 " ($sql) WHERE rownum <= :adodb_nrows". 620 ") WHERE adodb_rownum >= :adodb_offset"; 621 } 622 $inputarr['adodb_nrows'] = $nrows; 623 $inputarr['adodb_offset'] = $offset; 624 625 if ($secs2cache>0) $rs =& $this->CacheExecute($secs2cache, $sql,$inputarr); 626 else $rs =& $this->Execute($sql,$inputarr); 627 return $rs; 628 } 629 630 } 631 632 /** 633 * Usage: 634 * Store BLOBs and CLOBs 635 * 636 * Example: to store $var in a blob 637 * 638 * $conn->Execute('insert into TABLE (id,ablob) values(12,empty_blob())'); 639 * $conn->UpdateBlob('TABLE', 'ablob', $varHoldingBlob, 'ID=12', 'BLOB'); 640 * 641 * $blobtype supports 'BLOB' and 'CLOB', but you need to change to 'empty_clob()'. 642 * 643 * to get length of LOB: 644 * select DBMS_LOB.GETLENGTH(ablob) from TABLE 645 * 646 * If you are using CURSOR_SHARING = force, it appears this will case a segfault 647 * under oracle 8.1.7.0. Run: 648 * $db->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT'); 649 * before UpdateBlob() then... 650 */ 651 652 function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') 653 { 654 655 //if (strlen($val) < 4000) return $this->Execute("UPDATE $table SET $column=:blob WHERE $where",array('blob'=>$val)) != false; 656 657 switch(strtoupper($blobtype)) { 658 default: ADOConnection::outp("<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false; 659 case 'BLOB': $type = OCI_B_BLOB; break; 660 case 'CLOB': $type = OCI_B_CLOB; break; 661 } 662 663 if ($this->databaseType == 'oci8po') 664 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?"; 665 else 666 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob"; 667 668 $desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB); 669 $arr['blob'] = array($desc,-1,$type); 670 if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT'); 671 $commit = $this->autoCommit; 672 if ($commit) $this->BeginTrans(); 673 $rs = $this->Execute($sql,$arr); 674 if ($rez = !empty($rs)) $desc->save($val); 675 $desc->free(); 676 if ($commit) $this->CommitTrans(); 677 if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=FORCE'); 678 679 if ($rez) $rs->Close(); 680 return $rez; 681 } 682 683 /** 684 * Usage: store file pointed to by $var in a blob 685 */ 686 function UpdateBlobFile($table,$column,$val,$where,$blobtype='BLOB') 687 { 688 switch(strtoupper($blobtype)) { 689 default: ADOConnection::outp( "<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false; 690 case 'BLOB': $type = OCI_B_BLOB; break; 691 case 'CLOB': $type = OCI_B_CLOB; break; 692 } 693 694 if ($this->databaseType == 'oci8po') 695 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?"; 696 else 697 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob"; 698 699 $desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB); 700 $arr['blob'] = array($desc,-1,$type); 701 702 $this->BeginTrans(); 703 $rs = ADODB_oci8::Execute($sql,$arr); 704 if ($rez = !empty($rs)) $desc->savefile($val); 705 $desc->free(); 706 $this->CommitTrans(); 707 708 if ($rez) $rs->Close(); 709 return $rez; 710 } 711 712 /** 713 * Execute SQL 714 * 715 * @param sql SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text) 716 * @param [inputarr] holds the input data to bind to. Null elements will be set to null. 717 * @return RecordSet or false 718 */ 719 function &Execute($sql,$inputarr=false) 720 { 721 if ($this->fnExecute) { 722 $fn = $this->fnExecute; 723 $ret =& $fn($this,$sql,$inputarr); 724 if (isset($ret)) return $ret; 725 } 726 if ($inputarr) { 727 #if (!is_array($inputarr)) $inputarr = array($inputarr); 728 729 $element0 = reset($inputarr); 730 731 # is_object check because oci8 descriptors can be passed in 732 if (is_array($element0) && !is_object(reset($element0))) { 733 if (is_string($sql)) 734 $stmt = $this->Prepare($sql); 735 else 736 $stmt = $sql; 737 738 foreach($inputarr as $arr) { 739 $ret =& $this->_Execute($stmt,$arr); 740 if (!$ret) return $ret; 741 } 742 } else { 743 $ret =& $this->_Execute($sql,$inputarr); 744 } 745 746 } else { 747 $ret =& $this->_Execute($sql,false); 748 } 749 750 return $ret; 751 } 752 753 /* 754 Example of usage: 755 756 $stmt = $this->Prepare('insert into emp (empno, ename) values (:empno, :ename)'); 757 */ 758 function Prepare($sql,$cursor=false) 759 { 760 static $BINDNUM = 0; 761 762 $stmt = OCIParse($this->_connectionID,$sql); 763 764 if (!$stmt) return false; 765 766 $BINDNUM += 1; 767 768 $sttype = @OCIStatementType($stmt); 769 if ($sttype == 'BEGIN' || $sttype == 'DECLARE') { 770 return array($sql,$stmt,0,$BINDNUM, ($cursor) ? OCINewCursor($this->_connectionID) : false); 771 } 772 return array($sql,$stmt,0,$BINDNUM); 773 } 774 775 /* 776 Call an oracle stored procedure and returns a cursor variable as a recordset. 777 Concept by Robert Tuttle robert@ud.com 778 779 Example: 780 Note: we return a cursor variable in :RS2 781 $rs = $db->ExecuteCursor("BEGIN adodb.open_tab(:RS2); END;",'RS2'); 782 783 $rs = $db->ExecuteCursor( 784 "BEGIN :RS2 = adodb.getdata(:VAR1); END;", 785 'RS2', 786 array('VAR1' => 'Mr Bean')); 787 788 */ 789 function &ExecuteCursor($sql,$cursorName='rs',$params=false) 790 { 791 if (is_array($sql)) $stmt = $sql; 792 else $stmt = ADODB_oci8::Prepare($sql,true); # true to allocate OCINewCursor 793 794 if (is_array($stmt) && sizeof($stmt) >= 5) { 795 $hasref = true; 796 $this->Parameter($stmt, $ignoreCur, $cursorName, false, -1, OCI_B_CURSOR); 797 if ($params) { 798 foreach($params as $k => $v) { 799 $this->Parameter($stmt,$params[$k], $k); 800 } 801 } 802 } else 803 $hasref = false; 804 805 $rs =& $this->Execute($stmt); 806 if ($rs->databaseType == 'array') OCIFreeCursor($stmt[4]); 807 else if ($hasref) $rs->_refcursor = $stmt[4]; 808 return $rs; 809 } 810 811 /* 812 Bind a variable -- very, very fast for executing repeated statements in oracle. 813 Better than using 814 for ($i = 0; $i < $max; $i++) { 815 $p1 = ?; $p2 = ?; $p3 = ?; 816 $this->Execute("insert into table (col0, col1, col2) values (:0, :1, :2)", 817 array($p1,$p2,$p3)); 818 } 819 820 Usage: 821 $stmt = $DB->Prepare("insert into table (col0, col1, col2) values (:0, :1, :2)"); 822 $DB->Bind($stmt, $p1); 823 $DB->Bind($stmt, $p2); 824 $DB->Bind($stmt, $p3); 825 for ($i = 0; $i < $max; $i++) { 826 $p1 = ?; $p2 = ?; $p3 = ?; 827 $DB->Execute($stmt); 828 } 829 830 Some timings: 831 ** Test table has 3 cols, and 1 index. Test to insert 1000 records 832 Time 0.6081s (1644.60 inserts/sec) with direct OCIParse/OCIExecute 833 Time 0.6341s (1577.16 inserts/sec) with ADOdb Prepare/Bind/Execute 834 Time 1.5533s ( 643.77 inserts/sec) with pure SQL using Execute 835 836 Now if PHP only had batch/bulk updating like Java or PL/SQL... 837 838 Note that the order of parameters differs from OCIBindByName, 839 because we default the names to :0, :1, :2 840 */ 841 function Bind(&$stmt,&$var,$size=4000,$type=false,$name=false,$isOutput=false) 842 { 843 844 if (!is_array($stmt)) return false; 845 846 if (($type == OCI_B_CURSOR) && sizeof($stmt) >= 5) { 847 return OCIBindByName($stmt[1],":".$name,$stmt[4],$size,$type); 848 } 849 850 if ($name == false) { 851 if ($type !== false) $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size,$type); 852 else $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size); // +1 byte for null terminator 853 $stmt[2] += 1; 854 } else if (oci_lob_desc($type)) { 855 if ($this->debug) { 856 ADOConnection::outp("<b>Bind</b>: name = $name"); 857 } 858 //we have to create a new Descriptor here 859 $numlob = count($this->_refLOBs); 860 $this->_refLOBs[$numlob]['LOB'] = OCINewDescriptor($this->_connectionID, oci_lob_desc($type)); 861 $this->_refLOBs[$numlob]['TYPE'] = $isOutput; 862 863 $tmp = &$this->_refLOBs[$numlob]['LOB']; 864 $rez = OCIBindByName($stmt[1], ":".$name, $tmp, -1, $type); 865 if ($this->debug) { 866 ADOConnection::outp("<b>Bind</b>: descriptor has been allocated, var (".$name.") binded"); 867 } 868 869 // if type is input then write data to lob now 870 if ($isOutput == false) { 871 $var = $this->BlobEncode($var); 872 $tmp->WriteTemporary($var); 873 $this->_refLOBs[$numlob]['VAR'] = &$var; 874 if ($this->debug) { 875 ADOConnection::outp("<b>Bind</b>: LOB has been written to temp"); 876 } 877 } else { 878 $this->_refLOBs[$numlob]['VAR'] = &$var; 879 } 880 $rez = $tmp; 881 } else { 882 if ($this->debug) 883 ADOConnection::outp("<b>Bind</b>: name = $name"); 884 885 if ($type !== false) $rez = OCIBindByName($stmt[1],":".$name,$var,$size,$type); 886 else $rez = OCIBindByName($stmt[1],":".$name,$var,$size); // +1 byte for null terminator 887 } 888 889 return $rez; 890 } 891 892 function Param($name,$type=false) 893 { 894 return ':'.$name; 895 } 896 897 /* 898 Usage: 899 $stmt = $db->Prepare('select * from table where id =:myid and group=:group'); 900 $db->Parameter($stmt,$id,'myid'); 901 $db->Parameter($stmt,$group,'group'); 902 $db->Execute($stmt); 903 904 @param $stmt Statement returned by Prepare() or PrepareSP(). 905 @param $var PHP variable to bind to 906 @param $name Name of stored procedure variable name to bind to. 907 @param [$isOutput] Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8. 908 @param [$maxLen] Holds an maximum length of the variable. 909 @param [$type] The data type of $var. Legal values depend on driver. 910 911 See OCIBindByName documentation at php.net. 912 */ 913 function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false) 914 { 915 if ($this->debug) { 916 $prefix = ($isOutput) ? 'Out' : 'In'; 917 $ztype = (empty($type)) ? 'false' : $type; 918 ADOConnection::outp( "{$prefix}Parameter(\$stmt, \$php_var='$var', \$name='$name', \$maxLen=$maxLen, \$type=$ztype);"); 919 } 920 return $this->Bind($stmt,$var,$maxLen,$type,$name,$isOutput); 921 } 922 923 /* 924 returns query ID if successful, otherwise false 925 this version supports: 926 927 1. $db->execute('select * from table'); 928 929 2. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)'); 930 $db->execute($prepared_statement, array(1,2,3)); 931 932 3. $db->execute('insert into table (a,b,c) values (:a,:b,:c)',array('a'=>1,'b'=>2,'c'=>3)); 933 934 4. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)'); 935 $db->bind($stmt,1); $db->bind($stmt,2); $db->bind($stmt,3); 936 $db->execute($stmt); 937 */ 938 function _query($sql,$inputarr) 939 { 940 if (is_array($sql)) { // is prepared sql 941 $stmt = $sql[1]; 942 943 // we try to bind to permanent array, so that OCIBindByName is persistent 944 // and carried out once only - note that max array element size is 4000 chars 945 if (is_array($inputarr)) { 946 $bindpos = $sql[3]; 947 if (isset($this->_bind[$bindpos])) { 948 // all tied up already 949 $bindarr = &$this->_bind[$bindpos]; 950 } else { 951 // one statement to bind them all 952 $bindarr = array(); 953 foreach($inputarr as $k => $v) { 954 $bindarr[$k] = $v; 955 OCIBindByName($stmt,":$k",$bindarr[$k],is_string($v) && strlen($v)>4000 ? -1 : 4000); 956 } 957 $this->_bind[$bindpos] = &$bindarr; 958 } 959 } 960 } else { 961 $stmt=OCIParse($this->_connectionID,$sql); 962 } 963 964 $this->_stmt = $stmt; 965 if (!$stmt) return false; 966 967 if (defined('ADODB_PREFETCH_ROWS')) @OCISetPrefetch($stmt,ADODB_PREFETCH_ROWS); 968 969 if (is_array($inputarr)) { 970 foreach($inputarr as $k => $v) { 971 if (is_array($v)) { 972 if (sizeof($v) == 2) // suggested by g.giunta@libero. 973 OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]); 974 else 975 OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]); 976 977 if ($this->debug==99) echo "name=:$k",' var='.$inputarr[$k][0],' len='.$v[1],' type='.$v[2],'<br>'; 978 } else { 979 $len = -1; 980 if ($v === ' ') $len = 1; 981 if (isset($bindarr)) { // is prepared sql, so no need to ocibindbyname again 982 $bindarr[$k] = $v; 983 } else { // dynamic sql, so rebind every time 984 OCIBindByName($stmt,":$k",$inputarr[$k],$len); 985 } 986 } 987 } 988 } 989 990 $this->_errorMsg = false; 991 $this->_errorCode = false; 992 if (OCIExecute($stmt,$this->_commit)) { 993//OCIInternalDebug(1); 994 if (count($this -> _refLOBs) > 0) { 995 996 foreach ($this -> _refLOBs as $key => $value) { 997 if ($this -> _refLOBs[$key]['TYPE'] == true) { 998 $tmp = $this -> _refLOBs[$key]['LOB'] -> load(); 999 if ($this -> debug) { 1000 ADOConnection::outp("<b>OUT LOB</b>: LOB has been loaded. <br>"); 1001 } 1002 //$_GLOBALS[$this -> _refLOBs[$key]['VAR']] = $tmp; 1003 $this -> _refLOBs[$key]['VAR'] = $tmp; 1004 } else { 1005 $this->_refLOBs[$key]['LOB']->save($this->_refLOBs[$key]['VAR']); 1006 $this -> _refLOBs[$key]['LOB']->free(); 1007 unset($this -> _refLOBs[$key]); 1008 if ($this->debug) { 1009 ADOConnection::outp("<b>IN LOB</b>: LOB has been saved. <br>"); 1010 } 1011 } 1012 } 1013 } 1014 1015 switch (@OCIStatementType($stmt)) { 1016 case "SELECT": 1017 return $stmt; 1018 1019 case 'DECLARE': 1020 case "BEGIN": 1021 if (is_array($sql) && !empty($sql[4])) { 1022 $cursor = $sql[4]; 1023 if (is_resource($cursor)) { 1024 $ok = OCIExecute($cursor); 1025 return $cursor; 1026 } 1027 return $stmt; 1028 } else { 1029 if (is_resource($stmt)) { 1030 OCIFreeStatement($stmt); 1031 return true; 1032 } 1033 return $stmt; 1034 } 1035 break; 1036 default : 1037 // ociclose -- no because it could be used in a LOB? 1038 return true; 1039 } 1040 } 1041 return false; 1042 } 1043 1044 // returns true or false 1045 function _close() 1046 { 1047 if (!$this->_connectionID) return; 1048 1049 if (!$this->autoCommit) OCIRollback($this->_connectionID); 1050 if (count($this->_refLOBs) > 0) { 1051 foreach ($this ->_refLOBs as $key => $value) { 1052 $this->_refLOBs[$key]['LOB']->free(); 1053 unset($this->_refLOBs[$key]); 1054 } 1055 } 1056 OCILogoff($this->_connectionID); 1057 1058 $this->_stmt = false; 1059 $this->_connectionID = false; 1060 } 1061 1062 function MetaPrimaryKeys($table, $owner=false,$internalKey=false) 1063 { 1064 if ($internalKey) return array('ROWID'); 1065 1066 // tested with oracle 8.1.7 1067 $table = strtoupper($table); 1068 if ($owner) { 1069 $owner_clause = "AND ((a.OWNER = b.OWNER) AND (a.OWNER = UPPER('$owner')))"; 1070 $ptab = 'ALL_'; 1071 } else { 1072 $owner_clause = ''; 1073 $ptab = 'USER_'; 1074 } 1075 $sql = " 1076SELECT /*+ RULE */ distinct b.column_name 1077 FROM {$ptab}CONSTRAINTS a 1078 , {$ptab}CONS_COLUMNS b 1079 WHERE ( UPPER(b.table_name) = ('$table')) 1080 AND (UPPER(a.table_name) = ('$table') and a.constraint_type = 'P') 1081 $owner_clause 1082 AND (a.constraint_name = b.constraint_name)"; 1083 1084 $rs = $this->Execute($sql); 1085 if ($rs && !$rs->EOF) { 1086 $arr =& $rs->GetArray(); 1087 $a = array(); 1088 foreach($arr as $v) { 1089 $a[] = reset($v); 1090 } 1091 return $a; 1092 } 1093 else return false; 1094 } 1095 1096 // http://gis.mit.edu/classes/11.521/sqlnotes/referential_integrity.html 1097 function MetaForeignKeys($table, $owner=false) 1098 { 1099 global $ADODB_FETCH_MODE; 1100 1101 $save = $ADODB_FETCH_MODE; 1102 $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 1103 $table = $this->qstr(strtoupper($table)); 1104 if (!$owner) { 1105 $owner = $this->user; 1106 $tabp = 'user_'; 1107 } else 1108 $tabp = 'all_'; 1109 1110 $owner = ' and owner='.$this->qstr(strtoupper($owner)); 1111 1112 $sql = 1113"select constraint_name,r_owner,r_constraint_name 1114 from {$tabp}constraints 1115 where constraint_type = 'R' and table_name = $table $owner"; 1116 1117 $constraints =& $this->GetArray($sql); 1118 $arr = false; 1119 foreach($constraints as $constr) { 1120 $cons = $this->qstr($constr[0]); 1121 $rowner = $this->qstr($constr[1]); 1122 $rcons = $this->qstr($constr[2]); 1123 $cols = $this->GetArray("select column_name from {$tabp}cons_columns where constraint_name=$cons $owner order by position"); 1124 $tabcol = $this->GetArray("select table_name,column_name from {$tabp}cons_columns where owner=$rowner and constraint_name=$rcons order by position"); 1125 1126 if ($cols && $tabcol) 1127 for ($i=0, $max=sizeof($cols); $i < $max; $i++) { 1128 $arr[$tabcol[$i][0]] = $cols[$i][0].'='.$tabcol[$i][1]; 1129 } 1130 } 1131 $ADODB_FETCH_MODE = $save; 1132 1133 return $arr; 1134 } 1135 1136 1137 function CharMax() 1138 { 1139 return 4000; 1140 } 1141 1142 function TextMax() 1143 { 1144 return 4000; 1145 } 1146 1147 /** 1148 * Quotes a string. 1149 * An example is $db->qstr("Don't bother",magic_quotes_runtime()); 1150 * 1151 * @param s the string to quote 1152 * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc(). 1153 * This undoes the stupidity of magic quotes for GPC. 1154 * 1155 * @return quoted string to be sent back to database 1156 */ 1157 function qstr($s,$magic_quotes=false) 1158 { 1159 $nofixquotes=false; 1160 1161 if ($this->noNullStrings && strlen($s)==0)$s = ' '; 1162 if (!$magic_quotes) { 1163 if ($this->replaceQuote[0] == '\\'){ 1164 $s = str_replace('\\','\\\\',$s); 1165 } 1166 return "'".str_replace("'",$this->replaceQuote,$s)."'"; 1167 } 1168 1169 // undo magic quotes for " 1170 $s = str_replace('\\"','"',$s); 1171 1172 $s = str_replace('\\\\','\\',$s); 1173 return "'".str_replace("\\'",$this->replaceQuote,$s)."'"; 1174 1175 } 1176 1177} 1178 1179/*-------------------------------------------------------------------------------------- 1180 Class Name: Recordset 1181--------------------------------------------------------------------------------------*/ 1182 1183class ADORecordset_oci8 extends ADORecordSet { 1184 1185 var $databaseType = 'oci8'; 1186 var $bind=false; 1187 var $_fieldobjs; 1188 1189 //var $_arr = false; 1190 1191 function ADORecordset_oci8($queryID,$mode=false) 1192 { 1193 if ($mode === false) { 1194 global $ADODB_FETCH_MODE; 1195 $mode = $ADODB_FETCH_MODE; 1196 } 1197 switch ($mode) 1198 { 1199 case ADODB_FETCH_ASSOC:$this->fetchMode = OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break; 1200 case ADODB_FETCH_DEFAULT: 1201 case ADODB_FETCH_BOTH:$this->fetchMode = OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break; 1202 case ADODB_FETCH_NUM: 1203 default: 1204 $this->fetchMode = OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break; 1205 } 1206 1207 $this->adodbFetchMode = $mode; 1208 $this->_queryID = $queryID; 1209 } 1210 1211 1212 function Init() 1213 { 1214 if ($this->_inited) return; 1215 1216 $this->_inited = true; 1217 if ($this->_queryID) { 1218 1219 $this->_currentRow = 0; 1220 @$this->_initrs(); 1221 $this->EOF = !$this->_fetch(); 1222 1223 /* 1224 // based on idea by Gaetano Giunta to detect unusual oracle errors 1225 // see http://phplens.com/lens/lensforum/msgs.php?id=6771 1226 $err = OCIError($this->_queryID); 1227 if ($err && $this->connection->debug) ADOConnection::outp($err); 1228 */ 1229 1230 if (!is_array($this->fields)) { 1231 $this->_numOfRows = 0; 1232 $this->fields = array(); 1233 } 1234 } else { 1235 $this->fields = array(); 1236 $this->_numOfRows = 0; 1237 $this->_numOfFields = 0; 1238 $this->EOF = true; 1239 } 1240 } 1241 1242 function _initrs() 1243 { 1244 $this->_numOfRows = -1; 1245 $this->_numOfFields = OCInumcols($this->_queryID); 1246 if ($this->_numOfFields>0) { 1247 $this->_fieldobjs = array(); 1248 $max = $this->_numOfFields; 1249 for ($i=0;$i<$max; $i++) $this->_fieldobjs[] = $this->_FetchField($i); 1250 } 1251 } 1252 1253 /* Returns: an object containing field information. 1254 Get column information in the Recordset object. fetchField() can be used in order to obtain information about 1255 fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by 1256 fetchField() is retrieved. */ 1257 1258 function &_FetchField($fieldOffset = -1) 1259 { 1260 $fld = new ADOFieldObject; 1261 $fieldOffset += 1; 1262 $fld->name =OCIcolumnname($this->_queryID, $fieldOffset); 1263 $fld->type = OCIcolumntype($this->_queryID, $fieldOffset); 1264 $fld->max_length = OCIcolumnsize($this->_queryID, $fieldOffset); 1265 if ($fld->type == 'NUMBER') { 1266 $p = OCIColumnPrecision($this->_queryID, $fieldOffset); 1267 $sc = OCIColumnScale($this->_queryID, $fieldOffset); 1268 if ($p != 0 && $sc == 0) $fld->type = 'INT'; 1269 //echo " $this->name ($p.$sc) "; 1270 } 1271 return $fld; 1272 } 1273 1274 /* For some reason, OCIcolumnname fails when called after _initrs() so we cache it */ 1275 function &FetchField($fieldOffset = -1) 1276 { 1277 return $this->_fieldobjs[$fieldOffset]; 1278 } 1279 1280 1281 /* 1282 // 10% speedup to move MoveNext to child class 1283 function _MoveNext() 1284 { 1285 //global $ADODB_EXTENSION;if ($ADODB_EXTENSION) return @adodb_movenext($this); 1286 1287 if ($this->EOF) return false; 1288 1289 $this->_currentRow++; 1290 if(@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) 1291 return true; 1292 $this->EOF = true; 1293 1294 return false; 1295 } */ 1296 1297 1298 function MoveNext() 1299 { 1300 if (@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) { 1301 $this->_currentRow += 1; 1302 return true; 1303 } 1304 if (!$this->EOF) { 1305 $this->_currentRow += 1; 1306 $this->EOF = true; 1307 } 1308 return false; 1309 } 1310 1311 /* 1312 # does not work as first record is retrieved in _initrs(), so is not included in GetArray() 1313 function &GetArray($nRows = -1) 1314 { 1315 global $ADODB_OCI8_GETARRAY; 1316 1317 if (true || !empty($ADODB_OCI8_GETARRAY)) { 1318 # does not support $ADODB_ANSI_PADDING_OFF 1319 1320 //OCI_RETURN_NULLS and OCI_RETURN_LOBS is set by OCIfetchstatement 1321 switch($this->adodbFetchMode) { 1322 case ADODB_FETCH_NUM: 1323 1324 $ncols = @OCIfetchstatement($this->_queryID, $results, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW+OCI_NUM); 1325 $results = array_merge(array($this->fields),$results); 1326 return $results; 1327 1328 case ADODB_FETCH_ASSOC: 1329 if (ADODB_ASSOC_CASE != 2 || $this->databaseType != 'oci8') break; 1330 1331 $ncols = @OCIfetchstatement($this->_queryID, $assoc, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW); 1332 $results =& array_merge(array($this->fields),$assoc); 1333 return $results; 1334 1335 default: 1336 break; 1337 } 1338 } 1339 1340 $results =& ADORecordSet::GetArray($nRows); 1341 return $results; 1342 1343 } */ 1344 1345 /* Optimize SelectLimit() by using OCIFetch() instead of OCIFetchInto() */ 1346 function &GetArrayLimit($nrows,$offset=-1) 1347 { 1348 if ($offset <= 0) { 1349 $arr =& $this->GetArray($nrows); 1350 return $arr; 1351 } 1352 for ($i=1; $i < $offset; $i++) 1353 if (!@OCIFetch($this->_queryID)) return array(); 1354 1355 if (!@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) return array(); 1356 $results = array(); 1357 $cnt = 0; 1358 while (!$this->EOF && $nrows != $cnt) { 1359 $results[$cnt++] = $this->fields; 1360 $this->MoveNext(); 1361 } 1362 1363 return $results; 1364 } 1365 1366 1367 /* Use associative array to get fields array */ 1368 function Fields($colname) 1369 { 1370 if (!$this->bind) { 1371 $this->bind = array(); 1372 for ($i=0; $i < $this->_numOfFields; $i++) { 1373 $o = $this->FetchField($i); 1374 $this->bind[strtoupper($o->name)] = $i; 1375 } 1376 } 1377 1378 return $this->fields[$this->bind[strtoupper($colname)]]; 1379 } 1380 1381 1382 1383 function _seek($row) 1384 { 1385 return false; 1386 } 1387 1388 function _fetch() 1389 { 1390 return @OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode); 1391 } 1392 1393 /* close() only needs to be called if you are worried about using too much memory while your script 1394 is running. All associated result memory for the specified result identifier will automatically be freed. */ 1395 1396 function _close() 1397 { 1398 if ($this->connection->_stmt === $this->_queryID) $this->connection->_stmt = false; 1399 if (!empty($this->_refcursor)) { 1400 OCIFreeCursor($this->_refcursor); 1401 $this->_refcursor = false; 1402 } 1403 @OCIFreeStatement($this->_queryID); 1404 $this->_queryID = false; 1405 1406 } 1407 1408 function MetaType($t,$len=-1) 1409 { 1410 if (is_object($t)) { 1411 $fieldobj = $t; 1412 $t = $fieldobj->type; 1413 $len = $fieldobj->max_length; 1414 } 1415 switch (strtoupper($t)) { 1416 case 'VARCHAR': 1417 case 'VARCHAR2': 1418 case 'CHAR': 1419 case 'VARBINARY': 1420 case 'BINARY': 1421 case 'NCHAR': 1422 case 'NVARCHAR': 1423 case 'NVARCHAR2': 1424 if (isset($this) && $len <= $this->blobSize) return 'C'; 1425 1426 case 'NCLOB': 1427 case 'LONG': 1428 case 'LONG VARCHAR': 1429 case 'CLOB': 1430 return 'X'; 1431 1432 case 'LONG RAW': 1433 case 'LONG VARBINARY': 1434 case 'BLOB': 1435 return 'B'; 1436 1437 case 'DATE': 1438 return ($this->connection->datetime) ? 'T' : 'D'; 1439 1440 1441 case 'TIMESTAMP': return 'T'; 1442 1443 case 'INT': 1444 case 'SMALLINT': 1445 case 'INTEGER': 1446 return 'I'; 1447 1448 default: return 'N'; 1449 } 1450 } 1451} 1452 1453class ADORecordSet_ext_oci8 extends ADORecordSet_oci8 { 1454 function ADORecordSet_ext_oci8($queryID,$mode=false) 1455 { 1456 if ($mode === false) { 1457 global $ADODB_FETCH_MODE; 1458 $mode = $ADODB_FETCH_MODE; 1459 } 1460 switch ($mode) 1461 { 1462 case ADODB_FETCH_ASSOC:$this->fetchMode = OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break; 1463 case ADODB_FETCH_DEFAULT: 1464 case ADODB_FETCH_BOTH:$this->fetchMode = OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break; 1465 case ADODB_FETCH_NUM: 1466 default: $this->fetchMode = OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break; 1467 } 1468 $this->adodbFetchMode = $mode; 1469 $this->_queryID = $queryID; 1470 } 1471 1472 function MoveNext() 1473 { 1474 return adodb_movenext($this); 1475 } 1476} 1477?>