4 version V5.18 3 Sep 2012 (c) 2000-2012 John Lim. All rights reserved.
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.
10 Latest version is available at http://adodb.sourceforge.net
12 Code contributed by George Fourlanos <fou@infomap.gr>
14 13 Nov 2000 jlim - removed all ora_* references.
17 // security - hide paths
18 if (!defined('ADODB_DIR')) die();
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.
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.
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-2049), and years over 50 as years in
34 the 20th century (1950-1999). 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'
38 You can also modify the date format using the ALTER SESSION command.
41 # define the LOB descriptor type for the given type
42 # returns false if no LOB descriptor
43 function oci_lob_desc($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;
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'; // requires oracle 9 or later, otherwise use SYSDATE
62 var $metaDatabasesSQL = "SELECT USERNAME FROM ALL_USERS WHERE USERNAME NOT IN ('SYS','SYSTEM','DBSNMP','OUTLN') ORDER BY 1";
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 $metaColumnsSQL2 = "select column_name,data_type,data_length, data_scale, data_precision,
69 case when nullable = 'Y' then 'NULL'
70 else 'NOT NULL' end as nulls,
71 data_default from all_tab_cols
72 where owner='%s' and table_name='%s' order by column_id"; // when there is a schema
73 var $_bindInputArray = true;
75 var $_genIDSQL = "SELECT (%s.nextval) FROM DUAL";
78 PRAGMA AUTONOMOUS_TRANSACTION;
80 execute immediate 'CREATE SEQUENCE %s START WITH %s';
84 var $_dropSeqSQL = "DROP SEQUENCE %s";
85 var $hasAffectedRows = true;
86 var $random = "abs(mod(DBMS_RANDOM.RANDOM,10000001)/10000000)";
87 var $noNullStrings = false;
88 var $connectSID = false;
90 var $_nestedSQL = true;
91 var $_hasOciFetchStatement = false;
92 var $_getarray = false; // currently not working
93 var $leftOuter = ''; // oracle wierdness, $col = $value (+) for LEFT OUTER, $col (+)= $value for RIGHT OUTER
94 var $session_sharing_force_blob = false; // alter session on updateblob if set to true
95 var $firstrows = true; // enable first rows optimization on SelectLimit()
96 var $selectOffsetAlg1 = 1000; // when to use 1st algorithm of selectlimit.
97 var $NLS_DATE_FORMAT = 'YYYY-MM-DD'; // To include time, use 'RRRR-MM-DD HH24:MI:SS'
98 var $dateformat = 'YYYY-MM-DD'; // DBDate format
99 var $useDBDateFormatForTextInput=false;
100 var $datetime = false; // MetaType('DATE') returns 'D' (datetime==false) or 'T' (datetime == true)
101 var $_refLOBs = array();
103 // var $ansiOuter = true; // if oracle9
105 function ADODB_oci8()
107 $this->_hasOciFetchStatement = ADODB_PHPVER >= 0x4200;
108 if (defined('ADODB_EXTENSION')) $this->rsPrefix .= 'ext_';
111 /* function MetaColumns($table, $normalize=true) added by smondino@users.sourceforge.net*/
112 function MetaColumns($table, $normalize=true)
114 global $ADODB_FETCH_MODE;
117 $this->_findschema($table, $schema);
120 $save = $ADODB_FETCH_MODE;
121 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
122 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
125 $rs = $this->Execute(sprintf($this->metaColumnsSQL2, strtoupper($schema), strtoupper($table)));
127 $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table)));
129 if (isset($savem)) $this->SetFetchMode($savem);
130 $ADODB_FETCH_MODE = $save;
136 $fld = new ADOFieldObject();
137 $fld->name = $rs->fields[0];
138 $fld->type = $rs->fields[1];
139 $fld->max_length = $rs->fields[2];
140 $fld->scale = $rs->fields[3];
141 if ($rs->fields[1] == 'NUMBER') {
142 if ($rs->fields[3] == 0) $fld->type = 'INT';
143 $fld->max_length = $rs->fields[4];
145 $fld->not_null = (strncmp($rs->fields[5], 'NOT',3) === 0);
146 $fld->binary = (strpos($fld->type,'BLOB') !== false);
147 $fld->default_value = $rs->fields[6];
149 if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld;
150 else $retarr[strtoupper($fld->name)] = $fld;
162 $rs = $this->Execute("select TO_CHAR($this->sysTimeStamp,'YYYY-MM-DD HH24:MI:SS') from dual");
163 if ($rs && !$rs->EOF) return $this->UnixTimeStamp(reset($rs->fields));
170 Multiple modes of connection are supported:
173 $conn->Connect(false,'scott','tiger');
176 $conn->Connect(false,'scott','tiger',$tnsname);
177 $conn->Connect($tnsname,'scott','tiger');
179 c. Server + service name
180 $conn->Connect($serveraddress,'scott,'tiger',$service_name);
183 $conn->connectSID = true;
184 $conn->Connect($serveraddress,'scott,'tiger',$SID);
192 (ADDRESS = (PROTOCOL = TCP)(HOST = kermit)(PORT = 1523))
195 (SERVICE_NAME = natsoft.domain)
199 There are 3 connection modes, 0 = non-persistent, 1 = persistent, 2 = force new connection
202 function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$mode=0)
204 if (!function_exists('oci_pconnect')) return null;
207 $this->_errorMsg = false;
208 $this->_errorCode = false;
210 if($argHostname) { // added by Jorma Tuomainen <jorma.tuomainen@ppoy.fi>
211 if (empty($argDatabasename)) $argDatabasename = $argHostname;
213 if(strpos($argHostname,":")) {
214 $argHostinfo=explode(":",$argHostname);
215 $argHostname=$argHostinfo[0];
216 $argHostport=$argHostinfo[1];
218 $argHostport = empty($this->port)? "1521" : $this->port;
221 if (strncasecmp($argDatabasename,'SID=',4) == 0) {
222 $argDatabasename = substr($argDatabasename,4);
223 $this->connectSID = true;
226 if ($this->connectSID) {
227 $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
228 .")(PORT=$argHostport))(CONNECT_DATA=(SID=$argDatabasename)))";
230 $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
231 .")(PORT=$argHostport))(CONNECT_DATA=(SERVICE_NAME=$argDatabasename)))";
235 //if ($argHostname) print "<p>Connect: 1st argument should be left blank for $this->databaseType</p>";
237 $this->_connectionID = ($this->charSet) ?
238 oci_pconnect($argUsername,$argPassword, $argDatabasename,$this->charSet)
240 oci_pconnect($argUsername,$argPassword, $argDatabasename)
242 if ($this->_connectionID && $this->autoRollback) oci_rollback($this->_connectionID);
243 } else if ($mode==2) {
244 $this->_connectionID = ($this->charSet) ?
245 oci_new_connect($argUsername,$argPassword, $argDatabasename,$this->charSet)
247 oci_new_connect($argUsername,$argPassword, $argDatabasename);
250 $this->_connectionID = ($this->charSet) ?
251 oci_connect($argUsername,$argPassword, $argDatabasename,$this->charSet)
253 oci_connect($argUsername,$argPassword, $argDatabasename);
255 if (!$this->_connectionID) return false;
256 if ($this->_initdate) {
257 $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='".$this->NLS_DATE_FORMAT."'");
261 // Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production With the Partitioning option JServer Release 8.1.7.0.0 - Production
262 // $vers = oci_server_version($this->_connectionID);
263 // if (strpos($vers,'8i') !== false) $this->ansiOuter = true;
267 function ServerInfo()
269 $arr['compat'] = $this->GetOne('select value from sys.database_compatible_level');
270 $arr['description'] = @oci_server_version($this->_connectionID);
271 $arr['version'] = ADOConnection::_findvers($arr['description']);
274 // returns true or false
275 function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
277 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,1);
280 // returns true or false
281 function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
283 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,2);
286 function _affectedrows()
288 if (is_resource($this->_stmt)) return @oci_num_rows($this->_stmt);
292 function IfNull( $field, $ifNull )
294 return " NVL($field, $ifNull) "; // if Oracle
297 // format and return date string in database date format
298 function DBDate($d,$isfld=false)
300 if (empty($d) && $d !== 0) return 'null';
303 $d = _adodb_safedate($d);
304 return 'TO_DATE('.$d.",'".$this->dateformat."')";
307 if (is_string($d)) $d = ADORecordSet::UnixDate($d);
309 if (is_object($d)) $ds = $d->format($this->fmtDate);
310 else $ds = adodb_date($this->fmtDate,$d);
312 return "TO_DATE(".$ds.",'".$this->dateformat."')";
315 function BindDate($d)
317 $d = ADOConnection::DBDate($d);
318 if (strncmp($d,"'",1)) return $d;
320 return substr($d,1,strlen($d)-2);
323 function BindTimeStamp($ts)
325 if (empty($ts) && $ts !== 0) return 'null';
326 if (is_string($ts)) $ts = ADORecordSet::UnixTimeStamp($ts);
328 if (is_object($ts)) $tss = $ts->format("'Y-m-d H:i:s'");
329 else $tss = adodb_date("'Y-m-d H:i:s'",$ts);
334 // format and return date string in database timestamp format
335 function DBTimeStamp($ts,$isfld=false)
337 if (empty($ts) && $ts !== 0) return 'null';
338 if ($isfld) return 'TO_DATE(substr('.$ts.",1,19),'RRRR-MM-DD, HH24:MI:SS')";
339 if (is_string($ts)) $ts = ADORecordSet::UnixTimeStamp($ts);
341 if (is_object($ts)) $tss = $ts->format("'Y-m-d H:i:s'");
342 else $tss = date("'Y-m-d H:i:s'",$ts);
344 return 'TO_DATE('.$tss.",'RRRR-MM-DD, HH24:MI:SS')";
347 function RowLock($tables,$where,$col='1 as adodbignore')
349 if ($this->autoCommit) $this->BeginTrans();
350 return $this->GetOne("select $col from $tables where $where for update");
353 function MetaTables($ttype=false,$showSchema=false,$mask=false)
356 $save = $this->metaTablesSQL;
357 $mask = $this->qstr(strtoupper($mask));
358 $this->metaTablesSQL .= " AND upper(table_name) like $mask";
360 $ret = ADOConnection::MetaTables($ttype,$showSchema);
363 $this->metaTablesSQL = $save;
369 function MetaIndexes ($table, $primary = FALSE, $owner=false)
371 // save old fetch mode
372 global $ADODB_FETCH_MODE;
374 $save = $ADODB_FETCH_MODE;
375 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
377 if ($this->fetchMode !== FALSE) {
378 $savem = $this->SetFetchMode(FALSE);
382 $table = strtoupper($table);
388 $rs = $this->Execute(sprintf("SELECT * FROM ALL_CONSTRAINTS WHERE UPPER(TABLE_NAME)='%s' AND CONSTRAINT_TYPE='P'",$table));
389 if (!is_object($rs)) {
391 $this->SetFetchMode($savem);
392 $ADODB_FETCH_MODE = $save;
396 if ($row = $rs->FetchRow())
397 $primary_key = $row[1]; //constraint_name
399 if ($primary==TRUE && $primary_key=='') {
401 $this->SetFetchMode($savem);
402 $ADODB_FETCH_MODE = $save;
403 return $false; //There is no primary key
406 $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));
409 if (!is_object($rs)) {
411 $this->SetFetchMode($savem);
412 $ADODB_FETCH_MODE = $save;
417 // parse index data into array
419 while ($row = $rs->FetchRow()) {
420 if ($primary && $row[0] != $primary_key) continue;
421 if (!isset($indexes[$row[0]])) {
422 $indexes[$row[0]] = array(
423 'unique' => ($row[1] == 'UNIQUE'),
427 $indexes[$row[0]]['columns'][$row[2] - 1] = $row[3];
430 // sort columns by order in the index
431 foreach ( array_keys ($indexes) as $index ) {
432 ksort ($indexes[$index]['columns']);
436 $this->SetFetchMode($savem);
437 $ADODB_FETCH_MODE = $save;
442 function BeginTrans()
444 if ($this->transOff) return true;
445 $this->transCnt += 1;
446 $this->autoCommit = false;
447 $this->_commit = OCI_DEFAULT;
449 if ($this->_transmode) $ok = $this->Execute("SET TRANSACTION ".$this->_transmode);
452 return $ok ? true : false;
455 function CommitTrans($ok=true)
457 if ($this->transOff) return true;
458 if (!$ok) return $this->RollbackTrans();
460 if ($this->transCnt) $this->transCnt -= 1;
461 $ret = oci_commit($this->_connectionID);
462 $this->_commit = OCI_COMMIT_ON_SUCCESS;
463 $this->autoCommit = true;
467 function RollbackTrans()
469 if ($this->transOff) return true;
470 if ($this->transCnt) $this->transCnt -= 1;
471 $ret = oci_rollback($this->_connectionID);
472 $this->_commit = OCI_COMMIT_ON_SUCCESS;
473 $this->autoCommit = true;
478 function SelectDB($dbName)
485 if ($this->_errorMsg !== false) return $this->_errorMsg;
487 if (is_resource($this->_stmt)) $arr = @oci_error($this->_stmt);
489 if (is_resource($this->_connectionID)) $arr = @oci_error($this->_connectionID);
490 else $arr = @oci_error();
491 if ($arr === false) return '';
493 $this->_errorMsg = $arr['message'];
494 $this->_errorCode = $arr['code'];
495 return $this->_errorMsg;
500 if ($this->_errorCode !== false) return $this->_errorCode;
502 if (is_resource($this->_stmt)) $arr = @oci_error($this->_stmt);
504 $arr = @oci_error($this->_connectionID);
505 if ($arr == false) $arr = @oci_error();
506 if ($arr == false) return '';
509 $this->_errorMsg = $arr['message'];
510 $this->_errorCode = $arr['code'];
515 // Format date column in sql string given an input format that understands Y M D
516 function SQLDate($fmt, $col=false)
518 if (!$col) $col = $this->sysTimeStamp;
519 $s = 'TO_CHAR('.$col.",'";
522 for ($i=0; $i < $len; $i++) {
580 // handle escape characters...
583 $ch = substr($fmt,$i,1);
585 if (strpos('-/.:;, ',$ch) !== false) $s .= $ch;
586 else $s .= '"'.$ch.'"';
593 function GetRandRow($sql, $arr = false)
595 $sql = "SELECT * FROM ($sql ORDER BY dbms_random.value) WHERE rownum = 1";
597 return $this->GetRow($sql,$arr);
601 This algorithm makes use of
604 The FIRST_ROWS hint explicitly chooses the approach to optimize response time,
605 that is, minimum resource usage to return the first row. Results will be returned
606 as soon as they are identified.
608 b. Uses rownum tricks to obtain only the required rows from a given offset.
609 As this uses complicated sql statements, we only use this if the $offset >= 100.
610 This idea by Tomas V V Cox.
612 This implementation does not appear to work with oracle 8.0.5 or earlier. Comment
613 out this function then, and the slower SelectLimit() in the base class will be used.
615 function SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
617 // seems that oracle only supports 1 hint comment in 8i
618 if ($this->firstrows) {
619 if ($nrows > 500 && $nrows < 1000) $hint = "FIRST_ROWS($nrows)";
620 else $hint = 'FIRST_ROWS';
622 if (strpos($sql,'/*+') !== false)
623 $sql = str_replace('/*+ ',"/*+$hint ",$sql);
625 $sql = preg_replace('/^[ \t\n]*select/i',"SELECT /*+$hint*/",$sql);
628 if ($offset == -1 || ($offset < $this->selectOffsetAlg1 && 0 < $nrows && $nrows < 1000)) {
630 if ($offset > 0) $nrows += $offset;
631 //$inputarr['adodb_rownum'] = $nrows;
632 if ($this->databaseType == 'oci8po') {
633 $sql = "select * from (".$sql.") where rownum <= ?";
635 $sql = "select * from (".$sql.") where rownum <= :adodb_offset";
637 $inputarr['adodb_offset'] = $nrows;
640 // note that $nrows = 0 still has to work ==> no rows returned
642 $rs = ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
646 // Algorithm by Tomas V V Cox, from PEAR DB oci8.php
648 // Let Oracle return the name of the columns
649 $q_fields = "SELECT * FROM (".$sql.") WHERE NULL = NULL";
652 if (! $stmt_arr = $this->Prepare($q_fields)) {
655 $stmt = $stmt_arr[1];
657 if (is_array($inputarr)) {
658 foreach($inputarr as $k => $v) {
660 if (sizeof($v) == 2) // suggested by g.giunta@libero.
661 oci_bind_by_name($stmt,":$k",$inputarr[$k][0],$v[1]);
663 oci_bind_by_name($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
666 if ($v === ' ') $len = 1;
667 if (isset($bindarr)) { // is prepared sql, so no need to oci_bind_by_name again
669 } else { // dynamic sql, so rebind every time
670 oci_bind_by_name($stmt,":$k",$inputarr[$k],$len);
677 if (!oci_execute($stmt, OCI_DEFAULT)) {
678 oci_free_statement($stmt);
682 $ncols = oci_num_fields($stmt);
683 for ( $i = 1; $i <= $ncols; $i++ ) {
684 $cols[] = '"'.oci_field_name($stmt, $i).'"';
688 oci_free_statement($stmt);
689 $fields = implode(',', $cols);
690 if ($nrows <= 0) $nrows = 999999999999;
691 else $nrows += $offset;
692 $offset += 1; // in Oracle rownum starts at 1
694 if ($this->databaseType == 'oci8po') {
695 $sql = "SELECT /*+ FIRST_ROWS */ $fields FROM".
696 "(SELECT rownum as adodb_rownum, $fields FROM".
697 " ($sql) WHERE rownum <= ?".
698 ") WHERE adodb_rownum >= ?";
700 $sql = "SELECT /*+ FIRST_ROWS */ $fields FROM".
701 "(SELECT rownum as adodb_rownum, $fields FROM".
702 " ($sql) WHERE rownum <= :adodb_nrows".
703 ") WHERE adodb_rownum >= :adodb_offset";
705 $inputarr['adodb_nrows'] = $nrows;
706 $inputarr['adodb_offset'] = $offset;
708 if ($secs2cache>0) $rs = $this->CacheExecute($secs2cache, $sql,$inputarr);
709 else $rs = $this->Execute($sql,$inputarr);
717 * Store BLOBs and CLOBs
719 * Example: to store $var in a blob
721 * $conn->Execute('insert into TABLE (id,ablob) values(12,empty_blob())');
722 * $conn->UpdateBlob('TABLE', 'ablob', $varHoldingBlob, 'ID=12', 'BLOB');
724 * $blobtype supports 'BLOB' and 'CLOB', but you need to change to 'empty_clob()'.
726 * to get length of LOB:
727 * select DBMS_LOB.GETLENGTH(ablob) from TABLE
729 * If you are using CURSOR_SHARING = force, it appears this will case a segfault
730 * under oracle 8.1.7.0. Run:
731 * $db->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT');
732 * before UpdateBlob() then...
735 function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
738 //if (strlen($val) < 4000) return $this->Execute("UPDATE $table SET $column=:blob WHERE $where",array('blob'=>$val)) != false;
740 switch(strtoupper($blobtype)) {
741 default: ADOConnection::outp("<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
742 case 'BLOB': $type = OCI_B_BLOB; break;
743 case 'CLOB': $type = OCI_B_CLOB; break;
746 if ($this->databaseType == 'oci8po')
747 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
749 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
751 $desc = oci_new_descriptor($this->_connectionID, OCI_D_LOB);
752 $arr['blob'] = array($desc,-1,$type);
753 if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT');
754 $commit = $this->autoCommit;
755 if ($commit) $this->BeginTrans();
756 $rs = $this->_Execute($sql,$arr);
757 if ($rez = !empty($rs)) $desc->save($val);
759 if ($commit) $this->CommitTrans();
760 if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=FORCE');
762 if ($rez) $rs->Close();
767 * Usage: store file pointed to by $val in a blob
769 function UpdateBlobFile($table,$column,$val,$where,$blobtype='BLOB')
771 switch(strtoupper($blobtype)) {
772 default: ADOConnection::outp( "<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
773 case 'BLOB': $type = OCI_B_BLOB; break;
774 case 'CLOB': $type = OCI_B_CLOB; break;
777 if ($this->databaseType == 'oci8po')
778 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
780 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
782 $desc = oci_new_descriptor($this->_connectionID, OCI_D_LOB);
783 $arr['blob'] = array($desc,-1,$type);
786 $rs = ADODB_oci8::Execute($sql,$arr);
787 if ($rez = !empty($rs)) $desc->savefile($val);
789 $this->CommitTrans();
791 if ($rez) $rs->Close();
798 * @param sql SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text)
799 * @param [inputarr] holds the input data to bind to. Null elements will be set to null.
800 * @return RecordSet or false
802 function Execute($sql,$inputarr=false)
804 if ($this->fnExecute) {
805 $fn = $this->fnExecute;
806 $ret = $fn($this,$sql,$inputarr);
807 if (isset($ret)) return $ret;
810 #if (!is_array($inputarr)) $inputarr = array($inputarr);
812 $element0 = reset($inputarr);
813 $array2d = $this->bulkBind && is_array($element0) && !is_object(reset($element0));
815 # see http://phplens.com/lens/lensforum/msgs.php?id=18786
816 if ($array2d || !$this->_bindInputArray) {
818 # is_object check because oci8 descriptors can be passed in
819 if ($array2d && $this->_bindInputArray) {
821 $stmt = $this->Prepare($sql);
825 foreach($inputarr as $arr) {
826 $ret = $this->_Execute($stmt,$arr);
827 if (!$ret) return $ret;
831 $sqlarr = explode(':',$sql);
834 #var_dump($sqlarr);echo "<hr>";var_dump($inputarr);echo"<hr>";
835 foreach($sqlarr as $k => $str) {
836 if ($k == 0) { $sql = $str; continue; }
837 // we need $lastnomatch because of the following datetime,
838 // eg. '10:10:01', which causes code to think that there is bind param :10 and :1
839 $ok = preg_match('/^([0-9]*)/', $str, $arr);
841 if (!$ok) $sql .= $str;
844 if (isset($inputarr[$at]) || is_null($inputarr[$at])) {
845 if ((strlen($at) == strlen($str) && $k < sizeof($arr)-1)) {
848 } else if ($lastnomatch == $k-1) {
851 if (is_null($inputarr[$at])) $sql .= 'null';
852 else $sql .= $this->qstr($inputarr[$at]);
853 $sql .= substr($str, strlen($at));
864 $ret = $this->_Execute($sql,$inputarr);
868 $ret = $this->_Execute($sql,false);
877 $stmt = $this->Prepare('insert into emp (empno, ename) values (:empno, :ename)');
879 function Prepare($sql,$cursor=false)
883 $stmt = oci_parse($this->_connectionID,$sql);
886 $this->_errorMsg = false;
887 $this->_errorCode = false;
888 $arr = @oci_error($this->_connectionID);
889 if ($arr === false) return false;
891 $this->_errorMsg = $arr['message'];
892 $this->_errorCode = $arr['code'];
898 $sttype = @oci_statement_type($stmt);
899 if ($sttype == 'BEGIN' || $sttype == 'DECLARE') {
900 return array($sql,$stmt,0,$BINDNUM, ($cursor) ? oci_new_cursor($this->_connectionID) : false);
902 return array($sql,$stmt,0,$BINDNUM);
906 Call an oracle stored procedure and returns a cursor variable as a recordset.
907 Concept by Robert Tuttle robert@ud.com
910 Note: we return a cursor variable in :RS2
911 $rs = $db->ExecuteCursor("BEGIN adodb.open_tab(:RS2); END;",'RS2');
913 $rs = $db->ExecuteCursor(
914 "BEGIN :RS2 = adodb.getdata(:VAR1); END;",
916 array('VAR1' => 'Mr Bean'));
919 function ExecuteCursor($sql,$cursorName='rs',$params=false)
921 if (is_array($sql)) $stmt = $sql;
922 else $stmt = ADODB_oci8::Prepare($sql,true); # true to allocate oci_new_cursor
924 if (is_array($stmt) && sizeof($stmt) >= 5) {
927 $this->Parameter($stmt, $ignoreCur, $cursorName, false, -1, OCI_B_CURSOR);
929 foreach($params as $k => $v) {
930 $this->Parameter($stmt,$params[$k], $k);
936 $rs = $this->Execute($stmt);
938 if ($rs->databaseType == 'array') oci_free_cursor($stmt[4]);
939 else if ($hasref) $rs->_refcursor = $stmt[4];
945 Bind a variable -- very, very fast for executing repeated statements in oracle.
947 for ($i = 0; $i < $max; $i++) {
948 $p1 = ?; $p2 = ?; $p3 = ?;
949 $this->Execute("insert into table (col0, col1, col2) values (:0, :1, :2)",
954 $stmt = $DB->Prepare("insert into table (col0, col1, col2) values (:0, :1, :2)");
955 $DB->Bind($stmt, $p1);
956 $DB->Bind($stmt, $p2);
957 $DB->Bind($stmt, $p3);
958 for ($i = 0; $i < $max; $i++) {
959 $p1 = ?; $p2 = ?; $p3 = ?;
964 ** Test table has 3 cols, and 1 index. Test to insert 1000 records
965 Time 0.6081s (1644.60 inserts/sec) with direct oci_parse/oci_execute
966 Time 0.6341s (1577.16 inserts/sec) with ADOdb Prepare/Bind/Execute
967 Time 1.5533s ( 643.77 inserts/sec) with pure SQL using Execute
969 Now if PHP only had batch/bulk updating like Java or PL/SQL...
971 Note that the order of parameters differs from oci_bind_by_name,
972 because we default the names to :0, :1, :2
974 function Bind(&$stmt,&$var,$size=4000,$type=false,$name=false,$isOutput=false)
977 if (!is_array($stmt)) return false;
979 if (($type == OCI_B_CURSOR) && sizeof($stmt) >= 5) {
980 return oci_bind_by_name($stmt[1],":".$name,$stmt[4],$size,$type);
983 if ($name == false) {
984 if ($type !== false) $rez = oci_bind_by_name($stmt[1],":".$stmt[2],$var,$size,$type);
985 else $rez = oci_bind_by_name($stmt[1],":".$stmt[2],$var,$size); // +1 byte for null terminator
987 } else if (oci_lob_desc($type)) {
989 ADOConnection::outp("<b>Bind</b>: name = $name");
991 //we have to create a new Descriptor here
992 $numlob = count($this->_refLOBs);
993 $this->_refLOBs[$numlob]['LOB'] = oci_new_descriptor($this->_connectionID, oci_lob_desc($type));
994 $this->_refLOBs[$numlob]['TYPE'] = $isOutput;
996 $tmp = $this->_refLOBs[$numlob]['LOB'];
997 $rez = oci_bind_by_name($stmt[1], ":".$name, $tmp, -1, $type);
999 ADOConnection::outp("<b>Bind</b>: descriptor has been allocated, var (".$name.") binded");
1002 // if type is input then write data to lob now
1003 if ($isOutput == false) {
1004 $var = $this->BlobEncode($var);
1005 $tmp->WriteTemporary($var);
1006 $this->_refLOBs[$numlob]['VAR'] = &$var;
1008 ADOConnection::outp("<b>Bind</b>: LOB has been written to temp");
1011 $this->_refLOBs[$numlob]['VAR'] = &$var;
1016 ADOConnection::outp("<b>Bind</b>: name = $name");
1018 if ($type !== false) $rez = oci_bind_by_name($stmt[1],":".$name,$var,$size,$type);
1019 else $rez = oci_bind_by_name($stmt[1],":".$name,$var,$size); // +1 byte for null terminator
1025 function Param($name,$type='C')
1032 $stmt = $db->Prepare('select * from table where id =:myid and group=:group');
1033 $db->Parameter($stmt,$id,'myid');
1034 $db->Parameter($stmt,$group,'group');
1035 $db->Execute($stmt);
1037 @param $stmt Statement returned by Prepare() or PrepareSP().
1038 @param $var PHP variable to bind to
1039 @param $name Name of stored procedure variable name to bind to.
1040 @param [$isOutput] Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8.
1041 @param [$maxLen] Holds an maximum length of the variable.
1042 @param [$type] The data type of $var. Legal values depend on driver.
1044 See oci_bind_by_name documentation at php.net.
1046 function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
1049 $prefix = ($isOutput) ? 'Out' : 'In';
1050 $ztype = (empty($type)) ? 'false' : $type;
1051 ADOConnection::outp( "{$prefix}Parameter(\$stmt, \$php_var='$var', \$name='$name', \$maxLen=$maxLen, \$type=$ztype);");
1053 return $this->Bind($stmt,$var,$maxLen,$type,$name,$isOutput);
1057 returns query ID if successful, otherwise false
1058 this version supports:
1060 1. $db->execute('select * from table');
1062 2. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
1063 $db->execute($prepared_statement, array(1,2,3));
1065 3. $db->execute('insert into table (a,b,c) values (:a,:b,:c)',array('a'=>1,'b'=>2,'c'=>3));
1067 4. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
1068 $db->bind($stmt,1); $db->bind($stmt,2); $db->bind($stmt,3);
1069 $db->execute($stmt);
1071 function _query($sql,$inputarr=false)
1073 if (is_array($sql)) { // is prepared sql
1076 // we try to bind to permanent array, so that oci_bind_by_name is persistent
1077 // and carried out once only - note that max array element size is 4000 chars
1078 if (is_array($inputarr)) {
1080 if (isset($this->_bind[$bindpos])) {
1081 // all tied up already
1082 $bindarr = $this->_bind[$bindpos];
1084 // one statement to bind them all
1086 foreach($inputarr as $k => $v) {
1088 oci_bind_by_name($stmt,":$k",$bindarr[$k],is_string($v) && strlen($v)>4000 ? -1 : 4000);
1090 $this->_bind[$bindpos] = $bindarr;
1094 $stmt=oci_parse($this->_connectionID,$sql);
1097 $this->_stmt = $stmt;
1098 if (!$stmt) return false;
1100 if (defined('ADODB_PREFETCH_ROWS')) @oci_set_prefetch($stmt,ADODB_PREFETCH_ROWS);
1102 if (is_array($inputarr)) {
1103 foreach($inputarr as $k => $v) {
1105 if (sizeof($v) == 2) // suggested by g.giunta@libero.
1106 oci_bind_by_name($stmt,":$k",$inputarr[$k][0],$v[1]);
1108 oci_bind_by_name($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
1110 if ($this->debug==99) {
1111 if (is_object($v[0]))
1112 echo "name=:$k",' len='.$v[1],' type='.$v[2],'<br>';
1114 echo "name=:$k",' var='.$inputarr[$k][0],' len='.$v[1],' type='.$v[2],'<br>';
1119 if ($v === ' ') $len = 1;
1120 if (isset($bindarr)) { // is prepared sql, so no need to oci_bind_by_name again
1122 } else { // dynamic sql, so rebind every time
1123 oci_bind_by_name($stmt,":$k",$inputarr[$k],$len);
1129 $this->_errorMsg = false;
1130 $this->_errorCode = false;
1131 if (oci_execute($stmt,$this->_commit)) {
1133 if (count($this -> _refLOBs) > 0) {
1135 foreach ($this -> _refLOBs as $key => $value) {
1136 if ($this -> _refLOBs[$key]['TYPE'] == true) {
1137 $tmp = $this -> _refLOBs[$key]['LOB'] -> load();
1138 if ($this -> debug) {
1139 ADOConnection::outp("<b>OUT LOB</b>: LOB has been loaded. <br>");
1141 //$_GLOBALS[$this -> _refLOBs[$key]['VAR']] = $tmp;
1142 $this -> _refLOBs[$key]['VAR'] = $tmp;
1144 $this->_refLOBs[$key]['LOB']->save($this->_refLOBs[$key]['VAR']);
1145 $this -> _refLOBs[$key]['LOB']->free();
1146 unset($this -> _refLOBs[$key]);
1148 ADOConnection::outp("<b>IN LOB</b>: LOB has been saved. <br>");
1154 switch (@oci_statement_type($stmt)) {
1160 if (is_array($sql) && !empty($sql[4])) {
1162 if (is_resource($cursor)) {
1163 $ok = oci_execute($cursor);
1168 if (is_resource($stmt)) {
1169 oci_free_statement($stmt);
1183 // From Oracle Whitepaper: PHP Scalability and High Availability
1184 function IsConnectionError($err)
1187 case 378: /* buffer pool param incorrect */
1188 case 602: /* core dump */
1189 case 603: /* fatal error */
1190 case 609: /* attach failed */
1191 case 1012: /* not logged in */
1192 case 1033: /* init or shutdown in progress */
1193 case 1043: /* Oracle not available */
1194 case 1089: /* immediate shutdown in progress */
1195 case 1090: /* shutdown in progress */
1196 case 1092: /* instance terminated */
1197 case 3113: /* disconnect */
1198 case 3114: /* not connected */
1199 case 3122: /* closing window */
1200 case 3135: /* lost contact */
1201 case 12153: /* TNS: not connected */
1202 case 27146: /* fatal or instance terminated */
1203 case 28511: /* Lost RPC */
1209 // returns true or false
1212 if (!$this->_connectionID) return;
1214 if (!$this->autoCommit) oci_rollback($this->_connectionID);
1215 if (count($this->_refLOBs) > 0) {
1216 foreach ($this ->_refLOBs as $key => $value) {
1217 $this->_refLOBs[$key]['LOB']->free();
1218 unset($this->_refLOBs[$key]);
1221 oci_close($this->_connectionID);
1223 $this->_stmt = false;
1224 $this->_connectionID = false;
1227 function MetaPrimaryKeys($table, $owner=false,$internalKey=false)
1229 if ($internalKey) return array('ROWID');
1231 // tested with oracle 8.1.7
1232 $table = strtoupper($table);
1234 $owner_clause = "AND ((a.OWNER = b.OWNER) AND (a.OWNER = UPPER('$owner')))";
1241 SELECT /*+ RULE */ distinct b.column_name
1242 FROM {$ptab}CONSTRAINTS a
1243 , {$ptab}CONS_COLUMNS b
1244 WHERE ( UPPER(b.table_name) = ('$table'))
1245 AND (UPPER(a.table_name) = ('$table') and a.constraint_type = 'P')
1247 AND (a.constraint_name = b.constraint_name)";
1249 $rs = $this->Execute($sql);
1250 if ($rs && !$rs->EOF) {
1251 $arr = $rs->GetArray();
1253 foreach($arr as $v) {
1261 // http://gis.mit.edu/classes/11.521/sqlnotes/referential_integrity.html
1262 function MetaForeignKeys($table, $owner=false)
1264 global $ADODB_FETCH_MODE;
1266 $save = $ADODB_FETCH_MODE;
1267 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
1268 $table = $this->qstr(strtoupper($table));
1270 $owner = $this->user;
1275 $owner = ' and owner='.$this->qstr(strtoupper($owner));
1278 "select constraint_name,r_owner,r_constraint_name
1279 from {$tabp}constraints
1280 where constraint_type = 'R' and table_name = $table $owner";
1282 $constraints = $this->GetArray($sql);
1284 foreach($constraints as $constr) {
1285 $cons = $this->qstr($constr[0]);
1286 $rowner = $this->qstr($constr[1]);
1287 $rcons = $this->qstr($constr[2]);
1288 $cols = $this->GetArray("select column_name from {$tabp}cons_columns where constraint_name=$cons $owner order by position");
1289 $tabcol = $this->GetArray("select table_name,column_name from {$tabp}cons_columns where owner=$rowner and constraint_name=$rcons order by position");
1291 if ($cols && $tabcol)
1292 for ($i=0, $max=sizeof($cols); $i < $max; $i++) {
1293 $arr[$tabcol[$i][0]] = $cols[$i][0].'='.$tabcol[$i][1];
1296 $ADODB_FETCH_MODE = $save;
1314 * An example is $db->qstr("Don't bother",magic_quotes_runtime());
1316 * @param s the string to quote
1317 * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc().
1318 * This undoes the stupidity of magic quotes for GPC.
1320 * @return quoted string to be sent back to database
1322 function qstr($s,$magic_quotes=false)
1324 //$nofixquotes=false;
1326 if ($this->noNullStrings && strlen($s)==0)$s = ' ';
1327 if (!$magic_quotes) {
1328 if ($this->replaceQuote[0] == '\\'){
1329 $s = str_replace('\\','\\\\',$s);
1331 return "'".str_replace("'",$this->replaceQuote,$s)."'";
1334 // undo magic quotes for " unless sybase is on
1335 if (!ini_get('magic_quotes_sybase')) {
1336 $s = str_replace('\\"','"',$s);
1337 $s = str_replace('\\\\','\\',$s);
1338 return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
1346 /*--------------------------------------------------------------------------------------
1347 Class Name: Recordset
1348 --------------------------------------------------------------------------------------*/
1350 class ADORecordset_oci8 extends ADORecordSet {
1352 var $databaseType = 'oci8';
1356 //var $_arr = false;
1358 function ADORecordset_oci8($queryID,$mode=false)
1360 if ($mode === false) {
1361 global $ADODB_FETCH_MODE;
1362 $mode = $ADODB_FETCH_MODE;
1366 case ADODB_FETCH_ASSOC:$this->fetchMode = OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
1367 case ADODB_FETCH_DEFAULT:
1368 case ADODB_FETCH_BOTH:$this->fetchMode = OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
1369 case ADODB_FETCH_NUM:
1371 $this->fetchMode = OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
1374 $this->adodbFetchMode = $mode;
1375 $this->_queryID = $queryID;
1381 if ($this->_inited) return;
1383 $this->_inited = true;
1384 if ($this->_queryID) {
1386 $this->_currentRow = 0;
1388 if ($this->_numOfFields) $this->EOF = !$this->_fetch();
1389 else $this->EOF = true;
1392 // based on idea by Gaetano Giunta to detect unusual oracle errors
1393 // see http://phplens.com/lens/lensforum/msgs.php?id=6771
1394 $err = oci_error($this->_queryID);
1395 if ($err && $this->connection->debug) ADOConnection::outp($err);
1398 if (!is_array($this->fields)) {
1399 $this->_numOfRows = 0;
1400 $this->fields = array();
1403 $this->fields = array();
1404 $this->_numOfRows = 0;
1405 $this->_numOfFields = 0;
1412 $this->_numOfRows = -1;
1413 $this->_numOfFields = oci_num_fields($this->_queryID);
1414 if ($this->_numOfFields>0) {
1415 $this->_fieldobjs = array();
1416 $max = $this->_numOfFields;
1417 for ($i=0;$i<$max; $i++) $this->_fieldobjs[] = $this->_FetchField($i);
1421 /* Returns: an object containing field information.
1422 Get column information in the Recordset object. fetchField() can be used in order to obtain information about
1423 fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
1424 fetchField() is retrieved. */
1426 function _FetchField($fieldOffset = -1)
1428 $fld = new ADOFieldObject;
1430 $fld->name =oci_field_name($this->_queryID, $fieldOffset);
1431 $fld->type = oci_field_type($this->_queryID, $fieldOffset);
1432 $fld->max_length = oci_field_size($this->_queryID, $fieldOffset);
1433 switch($fld->type) {
1435 $p = oci_field_precision($this->_queryID, $fieldOffset);
1436 $sc = oci_field_scale($this->_queryID, $fieldOffset);
1437 if ($p != 0 && $sc == 0) $fld->type = 'INT';
1444 $fld->max_length = -1;
1450 /* For some reason, oci_field_name fails when called after _initrs() so we cache it */
1451 function FetchField($fieldOffset = -1)
1453 return $this->_fieldobjs[$fieldOffset];
1458 // 10% speedup to move MoveNext to child class
1459 function _MoveNext()
1461 //global $ADODB_EXTENSION;if ($ADODB_EXTENSION) return @adodb_movenext($this);
1463 if ($this->EOF) return false;
1465 $this->_currentRow++;
1466 if($this->fields = @oci_fetch_array($this->_queryID,$this->fetchMode))
1476 if ($this->fields = @oci_fetch_array($this->_queryID,$this->fetchMode)) {
1477 $this->_currentRow += 1;
1481 $this->_currentRow += 1;
1488 # does not work as first record is retrieved in _initrs(), so is not included in GetArray()
1489 function GetArray($nRows = -1)
1491 global $ADODB_OCI8_GETARRAY;
1493 if (true || !empty($ADODB_OCI8_GETARRAY)) {
1494 # does not support $ADODB_ANSI_PADDING_OFF
1496 //OCI_RETURN_NULLS and OCI_RETURN_LOBS is set by oci_fetch_all
1497 switch($this->adodbFetchMode) {
1498 case ADODB_FETCH_NUM:
1500 $ncols = @oci_fetch_all($this->_queryID, $results, 0, $nRows, oci_fetch_all_BY_ROW+OCI_NUM);
1501 $results = array_merge(array($this->fields),$results);
1504 case ADODB_FETCH_ASSOC:
1505 if (ADODB_ASSOC_CASE != 2 || $this->databaseType != 'oci8') break;
1507 $ncols = @oci_fetch_all($this->_queryID, $assoc, 0, $nRows, oci_fetch_all_BY_ROW);
1508 $results = array_merge(array($this->fields),$assoc);
1516 $results = ADORecordSet::GetArray($nRows);
1521 /* Optimize SelectLimit() by using oci_fetch() */
1522 function GetArrayLimit($nrows,$offset=-1)
1525 $arr = $this->GetArray($nrows);
1529 for ($i=1; $i < $offset; $i++)
1530 if (!@oci_fetch($this->_queryID)) return $arr;
1532 if (!$this->fields = @oci_fetch_array($this->_queryID,$this->fetchMode)) return $arr;;
1535 while (!$this->EOF && $nrows != $cnt) {
1536 $results[$cnt++] = $this->fields;
1544 /* Use associative array to get fields array */
1545 function Fields($colname)
1548 $this->bind = array();
1549 for ($i=0; $i < $this->_numOfFields; $i++) {
1550 $o = $this->FetchField($i);
1551 $this->bind[strtoupper($o->name)] = $i;
1555 return $this->fields[$this->bind[strtoupper($colname)]];
1560 function _seek($row)
1567 return $this->fields = @oci_fetch_array($this->_queryID,$this->fetchMode);
1570 /* close() only needs to be called if you are worried about using too much memory while your script
1571 is running. All associated result memory for the specified result identifier will automatically be freed. */
1575 if ($this->connection->_stmt === $this->_queryID) $this->connection->_stmt = false;
1576 if (!empty($this->_refcursor)) {
1577 oci_free_cursor($this->_refcursor);
1578 $this->_refcursor = false;
1580 @oci_free_statement($this->_queryID);
1581 $this->_queryID = false;
1585 function MetaType($t,$len=-1)
1587 if (is_object($t)) {
1589 $t = $fieldobj->type;
1590 $len = $fieldobj->max_length;
1592 switch (strtoupper($t)) {
1601 if ($len <= $this->blobSize) return 'C';
1605 case 'LONG VARCHAR':
1610 case 'LONG VARBINARY':
1615 return ($this->connection->datetime) ? 'T' : 'D';
1618 case 'TIMESTAMP': return 'T';
1625 default: return 'N';
1630 class ADORecordSet_ext_oci8 extends ADORecordSet_oci8 {
1631 function ADORecordSet_ext_oci8($queryID,$mode=false)
1633 if ($mode === false) {
1634 global $ADODB_FETCH_MODE;
1635 $mode = $ADODB_FETCH_MODE;
1639 case ADODB_FETCH_ASSOC:$this->fetchMode = OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
1640 case ADODB_FETCH_DEFAULT:
1641 case ADODB_FETCH_BOTH:$this->fetchMode = OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
1642 case ADODB_FETCH_NUM:
1643 default: $this->fetchMode = OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
1645 $this->adodbFetchMode = $mode;
1646 $this->_queryID = $queryID;
1651 return adodb_movenext($this);