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