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