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