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