4 version V4.98 13 Feb 2008 (c) 2000-2008 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';
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 $_bindInputArray = 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;
78 var $_nestedSQL = true;
79 var $_hasOCIFetchStatement = false;
80 var $_getarray = false; // currently not working
81 var $leftOuter = ''; // oracle wierdness, $col = $value (+) for LEFT OUTER, $col (+)= $value for RIGHT OUTER
82 var $session_sharing_force_blob = false; // alter session on updateblob if set to true
83 var $firstrows = true; // enable first rows optimization on SelectLimit()
84 var $selectOffsetAlg1 = 100; // when to use 1st algorithm of selectlimit.
85 var $NLS_DATE_FORMAT = 'YYYY-MM-DD'; // To include time, use 'RRRR-MM-DD HH24:MI:SS'
86 var $dateformat = 'YYYY-MM-DD'; // for DBDate()
87 var $useDBDateFormatForTextInput=false;
88 var $datetime = false; // MetaType('DATE') returns 'D' (datetime==false) or 'T' (datetime == true)
89 var $_refLOBs = array();
91 // var $ansiOuter = true; // if oracle9
95 $this->_hasOCIFetchStatement
= ADODB_PHPVER
>= 0x4200;
96 if (defined('ADODB_EXTENSION')) $this->rsPrefix
.= 'ext_';
99 /* Function &MetaColumns($table) added by smondino@users.sourceforge.net*/
100 function &MetaColumns($table)
102 global $ADODB_FETCH_MODE;
105 $save = $ADODB_FETCH_MODE;
106 $ADODB_FETCH_MODE = ADODB_FETCH_NUM
;
107 if ($this->fetchMode
!== false) $savem = $this->SetFetchMode(false);
109 $rs = $this->Execute(sprintf($this->metaColumnsSQL
,strtoupper($table)));
111 if (isset($savem)) $this->SetFetchMode($savem);
112 $ADODB_FETCH_MODE = $save;
117 while (!$rs->EOF
) { //print_r($rs->fields);
118 $fld = new ADOFieldObject();
119 $fld->name
= $rs->fields
[0];
120 $fld->type
= $rs->fields
[1];
121 $fld->max_length
= $rs->fields
[2];
122 $fld->scale
= $rs->fields
[3];
123 if ($rs->fields
[1] == 'NUMBER') {
124 if ($rs->fields
[3] == 0) $fld->type
= 'INT';
125 $fld->max_length
= $rs->fields
[4];
127 $fld->not_null
= (strncmp($rs->fields
[5], 'NOT',3) === 0);
128 $fld->binary
= (strpos($fld->type
,'BLOB') !== false);
129 $fld->default_value
= $rs->fields
[6];
131 if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM
) $retarr[] = $fld;
132 else $retarr[strtoupper($fld->name
)] = $fld;
144 $rs =& $this->Execute("select TO_CHAR($this->sysTimeStamp,'YYYY-MM-DD HH24:MI:SS') from dual");
145 if ($rs && !$rs->EOF
) return $this->UnixTimeStamp(reset($rs->fields
));
152 Multiple modes of connection are supported:
155 $conn->Connect(false,'scott','tiger');
158 $conn->Connect(false,'scott','tiger',$tnsname);
159 $conn->Connect($tnsname,'scott','tiger');
161 c. Server + service name
162 $conn->Connect($serveraddress,'scott,'tiger',$service_name);
165 $conn->connectSID = true;
166 $conn->Connect($serveraddress,'scott,'tiger',$SID);
174 (ADDRESS = (PROTOCOL = TCP)(HOST = kermit)(PORT = 1523))
177 (SERVICE_NAME = natsoft.domain)
181 There are 3 connection modes, 0 = non-persistent, 1 = persistent, 2 = force new connection
184 function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$mode=0)
186 if (!function_exists('OCIPLogon')) return null;
189 $this->_errorMsg
= false;
190 $this->_errorCode
= false;
192 if($argHostname) { // added by Jorma Tuomainen <jorma.tuomainen@ppoy.fi>
193 if (empty($argDatabasename)) $argDatabasename = $argHostname;
195 if(strpos($argHostname,":")) {
196 $argHostinfo=explode(":",$argHostname);
197 $argHostname=$argHostinfo[0];
198 $argHostport=$argHostinfo[1];
200 $argHostport = empty($this->port
)?
"1521" : $this->port
;
203 if ($this->connectSID
) {
204 $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
205 .")(PORT=$argHostport))(CONNECT_DATA=(SID=$argDatabasename)))";
207 $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
208 .")(PORT=$argHostport))(CONNECT_DATA=(SERVICE_NAME=$argDatabasename)))";
212 //if ($argHostname) print "<p>Connect: 1st argument should be left blank for $this->databaseType</p>";
214 $this->_connectionID
= ($this->charSet
) ?
215 OCIPLogon($argUsername,$argPassword, $argDatabasename,$this->charSet
)
217 OCIPLogon($argUsername,$argPassword, $argDatabasename)
219 if ($this->_connectionID
&& $this->autoRollback
) OCIrollback($this->_connectionID
);
220 } else if ($mode==2) {
221 $this->_connectionID
= ($this->charSet
) ?
222 OCINLogon($argUsername,$argPassword, $argDatabasename,$this->charSet
)
224 OCINLogon($argUsername,$argPassword, $argDatabasename);
227 $this->_connectionID
= ($this->charSet
) ?
228 OCILogon($argUsername,$argPassword, $argDatabasename,$this->charSet
)
230 OCILogon($argUsername,$argPassword, $argDatabasename);
232 if (!$this->_connectionID
) return false;
233 if ($this->_initdate
) {
234 $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='".$this->NLS_DATE_FORMAT
."'");
238 // Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production With the Partitioning option JServer Release 8.1.7.0.0 - Production
239 // $vers = OCIServerVersion($this->_connectionID);
240 // if (strpos($vers,'8i') !== false) $this->ansiOuter = true;
244 function ServerInfo()
246 $arr['compat'] = $this->GetOne('select value from sys.database_compatible_level');
247 $arr['description'] = @OCIServerVersion
($this->_connectionID
);
248 $arr['version'] = ADOConnection
::_findvers($arr['description']);
251 // returns true or false
252 function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
254 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,1);
257 // returns true or false
258 function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
260 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,2);
263 function _affectedrows()
265 if (is_resource($this->_stmt
)) return @OCIRowCount
($this->_stmt
);
269 function IfNull( $field, $ifNull )
271 return " NVL($field, $ifNull) "; // if Oracle
274 // format and return date string in database date format
277 if (empty($d) && $d !== 0) return 'null';
279 if (is_string($d)) $d = ADORecordSet
::UnixDate($d);
280 return "TO_DATE(".adodb_date($this->fmtDate
,$d).",'".$this->dateformat
."')";
283 function BindDate($d)
285 $d = ADOConnection
::DBDate($d);
286 if (strncmp($d,"'",1)) return $d;
288 return substr($d,1,strlen($d)-2);
291 function BindTimeStamp($d)
293 $d = ADOConnection
::DBTimeStamp($d);
294 if (strncmp($d,"'",1)) return $d;
296 return substr($d,1,strlen($d)-2);
299 // format and return date string in database timestamp format
300 function DBTimeStamp($ts)
302 if (empty($ts) && $ts !== 0) return 'null';
303 if (is_string($ts)) $ts = ADORecordSet
::UnixTimeStamp($ts);
304 return 'TO_DATE('.adodb_date("'Y-m-d H:i:s'",$ts).",'RRRR-MM-DD, HH24:MI:SS')";
307 function RowLock($tables,$where,$flds='1 as ignore')
309 if ($this->autoCommit
) $this->BeginTrans();
310 return $this->GetOne("select $flds from $tables where $where for update");
313 function &MetaTables($ttype=false,$showSchema=false,$mask=false)
316 $save = $this->metaTablesSQL
;
317 $mask = $this->qstr(strtoupper($mask));
318 $this->metaTablesSQL
.= " AND upper(table_name) like $mask";
320 $ret =& ADOConnection
::MetaTables($ttype,$showSchema);
323 $this->metaTablesSQL
= $save;
329 function &MetaIndexes ($table, $primary = FALSE, $owner=false)
331 // save old fetch mode
332 global $ADODB_FETCH_MODE;
334 $save = $ADODB_FETCH_MODE;
335 $ADODB_FETCH_MODE = ADODB_FETCH_NUM
;
337 if ($this->fetchMode
!== FALSE) {
338 $savem = $this->SetFetchMode(FALSE);
342 $table = strtoupper($table);
348 $rs = $this->Execute(sprintf("SELECT * FROM ALL_CONSTRAINTS WHERE UPPER(TABLE_NAME)='%s' AND CONSTRAINT_TYPE='P'",$table));
349 if ($row = $rs->FetchRow())
350 $primary_key = $row[1]; //constraint_name
352 if ($primary==TRUE && $primary_key=='') {
354 $this->SetFetchMode($savem);
355 $ADODB_FETCH_MODE = $save;
356 return $false; //There is no primary key
359 $rs = $this->Execute(sprintf("SELECT ALL_INDEXES.INDEX_NAME, ALL_INDEXES.UNIQUENESS, ALL_IND_COLUMNS.COLUMN_POSITION, ALL_IND_COLUMNS.COLUMN_NAME FROM ALL_INDEXES,ALL_IND_COLUMNS WHERE UPPER(ALL_INDEXES.TABLE_NAME)='%s' AND ALL_IND_COLUMNS.INDEX_NAME=ALL_INDEXES.INDEX_NAME",$table));
362 if (!is_object($rs)) {
364 $this->SetFetchMode($savem);
365 $ADODB_FETCH_MODE = $save;
370 // parse index data into array
372 while ($row = $rs->FetchRow()) {
373 if ($primary && $row[0] != $primary_key) continue;
374 if (!isset($indexes[$row[0]])) {
375 $indexes[$row[0]] = array(
376 'unique' => ($row[1] == 'UNIQUE'),
380 $indexes[$row[0]]['columns'][$row[2] - 1] = $row[3];
383 // sort columns by order in the index
384 foreach ( array_keys ($indexes) as $index ) {
385 ksort ($indexes[$index]['columns']);
389 $this->SetFetchMode($savem);
390 $ADODB_FETCH_MODE = $save;
395 function BeginTrans()
397 if ($this->transOff
) return true;
398 $this->transCnt +
= 1;
399 $this->autoCommit
= false;
400 $this->_commit
= OCI_DEFAULT
;
402 if ($this->_transmode
) $this->Execute("SET TRANSACTION ".$this->_transmode
);
406 function CommitTrans($ok=true)
408 if ($this->transOff
) return true;
409 if (!$ok) return $this->RollbackTrans();
411 if ($this->transCnt
) $this->transCnt
-= 1;
412 $ret = OCIcommit($this->_connectionID
);
413 $this->_commit
= OCI_COMMIT_ON_SUCCESS
;
414 $this->autoCommit
= true;
418 function RollbackTrans()
420 if ($this->transOff
) return true;
421 if ($this->transCnt
) $this->transCnt
-= 1;
422 $ret = OCIrollback($this->_connectionID
);
423 $this->_commit
= OCI_COMMIT_ON_SUCCESS
;
424 $this->autoCommit
= true;
429 function SelectDB($dbName)
436 if ($this->_errorMsg
!== false) return $this->_errorMsg
;
438 if (is_resource($this->_stmt
)) $arr = @OCIError
($this->_stmt
);
440 if (is_resource($this->_connectionID
)) $arr = @OCIError
($this->_connectionID
);
441 else $arr = @OCIError
();
442 if ($arr === false) return '';
444 $this->_errorMsg
= $arr['message'];
445 $this->_errorCode
= $arr['code'];
446 return $this->_errorMsg
;
451 if ($this->_errorCode
!== false) return $this->_errorCode
;
453 if (is_resource($this->_stmt
)) $arr = @OCIError
($this->_stmt
);
455 $arr = @OCIError
($this->_connectionID
);
456 if ($arr == false) $arr = @OCIError
();
457 if ($arr == false) return '';
460 $this->_errorMsg
= $arr['message'];
461 $this->_errorCode
= $arr['code'];
466 // Format date column in sql string given an input format that understands Y M D
467 function SQLDate($fmt, $col=false)
469 if (!$col) $col = $this->sysTimeStamp
;
470 $s = 'TO_CHAR('.$col.",'";
473 for ($i=0; $i < $len; $i++
) {
531 // handle escape characters...
534 $ch = substr($fmt,$i,1);
536 if (strpos('-/.:;, ',$ch) !== false) $s .= $ch;
537 else $s .= '"'.$ch.'"';
544 function GetRandRow($sql, $arr = false)
546 $sql = "SELECT * FROM ($sql ORDER BY dbms_random.value) WHERE rownum = 1";
548 return $this->GetRow($sql,$arr);
552 This algorithm makes use of
555 The FIRST_ROWS hint explicitly chooses the approach to optimize response time,
556 that is, minimum resource usage to return the first row. Results will be returned
557 as soon as they are identified.
559 b. Uses rownum tricks to obtain only the required rows from a given offset.
560 As this uses complicated sql statements, we only use this if the $offset >= 100.
561 This idea by Tomas V V Cox.
563 This implementation does not appear to work with oracle 8.0.5 or earlier. Comment
564 out this function then, and the slower SelectLimit() in the base class will be used.
566 function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
568 // seems that oracle only supports 1 hint comment in 8i
569 if ($this->firstrows
) {
570 if (strpos($sql,'/*+') !== false)
571 $sql = str_replace('/*+ ','/*+FIRST_ROWS ',$sql);
573 $sql = preg_replace('/^[ \t\n]*select/i','SELECT /*+FIRST_ROWS*/',$sql);
576 if ($offset < $this->selectOffsetAlg1
&& 0 < $nrows && $nrows < 1000) {
578 if ($offset > 0) $nrows +
= $offset;
579 //$inputarr['adodb_rownum'] = $nrows;
580 if ($this->databaseType
== 'oci8po') {
581 $sql = "select * from (".$sql.") where rownum <= ?";
583 $sql = "select * from (".$sql.") where rownum <= :adodb_offset";
585 $inputarr['adodb_offset'] = $nrows;
588 // note that $nrows = 0 still has to work ==> no rows returned
590 $rs =& ADOConnection
::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
594 // Algorithm by Tomas V V Cox, from PEAR DB oci8.php
596 // Let Oracle return the name of the columns
597 $q_fields = "SELECT * FROM (".$sql.") WHERE NULL = NULL";
600 if (! $stmt_arr = $this->Prepare($q_fields)) {
603 $stmt = $stmt_arr[1];
605 if (is_array($inputarr)) {
606 foreach($inputarr as $k => $v) {
608 if (sizeof($v) == 2) // suggested by g.giunta@libero.
609 OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]);
611 OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
614 if ($v === ' ') $len = 1;
615 if (isset($bindarr)) { // is prepared sql, so no need to ocibindbyname again
617 } else { // dynamic sql, so rebind every time
618 OCIBindByName($stmt,":$k",$inputarr[$k],$len);
624 if (!OCIExecute($stmt, OCI_DEFAULT
)) {
625 OCIFreeStatement($stmt);
629 $ncols = OCINumCols($stmt);
630 for ( $i = 1; $i <= $ncols; $i++
) {
631 $cols[] = '"'.OCIColumnName($stmt, $i).'"';
635 OCIFreeStatement($stmt);
636 $fields = implode(',', $cols);
638 $offset +
= 1; // in Oracle rownum starts at 1
640 if ($this->databaseType
== 'oci8po') {
641 $sql = "SELECT $fields FROM".
642 "(SELECT rownum as adodb_rownum, $fields FROM".
643 " ($sql) WHERE rownum <= ?".
644 ") WHERE adodb_rownum >= ?";
646 $sql = "SELECT $fields FROM".
647 "(SELECT rownum as adodb_rownum, $fields FROM".
648 " ($sql) WHERE rownum <= :adodb_nrows".
649 ") WHERE adodb_rownum >= :adodb_offset";
651 $inputarr['adodb_nrows'] = $nrows;
652 $inputarr['adodb_offset'] = $offset;
654 if ($secs2cache>0) $rs =& $this->CacheExecute($secs2cache, $sql,$inputarr);
655 else $rs =& $this->Execute($sql,$inputarr);
663 * Store BLOBs and CLOBs
665 * Example: to store $var in a blob
667 * $conn->Execute('insert into TABLE (id,ablob) values(12,empty_blob())');
668 * $conn->UpdateBlob('TABLE', 'ablob', $varHoldingBlob, 'ID=12', 'BLOB');
670 * $blobtype supports 'BLOB' and 'CLOB', but you need to change to 'empty_clob()'.
672 * to get length of LOB:
673 * select DBMS_LOB.GETLENGTH(ablob) from TABLE
675 * If you are using CURSOR_SHARING = force, it appears this will case a segfault
676 * under oracle 8.1.7.0. Run:
677 * $db->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT');
678 * before UpdateBlob() then...
681 function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
684 //if (strlen($val) < 4000) return $this->Execute("UPDATE $table SET $column=:blob WHERE $where",array('blob'=>$val)) != false;
686 switch(strtoupper($blobtype)) {
687 default: ADOConnection
::outp("<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
688 case 'BLOB': $type = OCI_B_BLOB
; break;
689 case 'CLOB': $type = OCI_B_CLOB
; break;
692 if ($this->databaseType
== 'oci8po')
693 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
695 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
697 $desc = OCINewDescriptor($this->_connectionID
, OCI_D_LOB
);
698 $arr['blob'] = array($desc,-1,$type);
699 if ($this->session_sharing_force_blob
) $this->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT');
700 $commit = $this->autoCommit
;
701 if ($commit) $this->BeginTrans();
702 $rs = $this->_Execute($sql,$arr);
703 if ($rez = !empty($rs)) $desc->save($val);
705 if ($commit) $this->CommitTrans();
706 if ($this->session_sharing_force_blob
) $this->Execute('ALTER SESSION SET CURSOR_SHARING=FORCE');
708 if ($rez) $rs->Close();
713 * Usage: store file pointed to by $var in a blob
715 function UpdateBlobFile($table,$column,$val,$where,$blobtype='BLOB')
717 switch(strtoupper($blobtype)) {
718 default: ADOConnection
::outp( "<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
719 case 'BLOB': $type = OCI_B_BLOB
; break;
720 case 'CLOB': $type = OCI_B_CLOB
; break;
723 if ($this->databaseType
== 'oci8po')
724 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
726 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
728 $desc = OCINewDescriptor($this->_connectionID
, OCI_D_LOB
);
729 $arr['blob'] = array($desc,-1,$type);
732 $rs = ADODB_oci8
::Execute($sql,$arr);
733 if ($rez = !empty($rs)) $desc->savefile($val);
735 $this->CommitTrans();
737 if ($rez) $rs->Close();
744 * @param sql SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text)
745 * @param [inputarr] holds the input data to bind to. Null elements will be set to null.
746 * @return RecordSet or false
748 function &Execute($sql,$inputarr=false)
750 if ($this->fnExecute
) {
751 $fn = $this->fnExecute
;
752 $ret =& $fn($this,$sql,$inputarr);
753 if (isset($ret)) return $ret;
756 #if (!is_array($inputarr)) $inputarr = array($inputarr);
757 $element0 = reset($inputarr);
759 # is_object check because oci8 descriptors can be passed in
760 if (is_array($element0) && !is_object(reset($element0))) {
762 $stmt = $this->Prepare($sql);
766 foreach($inputarr as $arr) {
767 $ret =& $this->_Execute($stmt,$arr);
768 if (!$ret) return $ret;
771 $ret =& $this->_Execute($sql,$inputarr);
775 $ret =& $this->_Execute($sql,false);
784 $stmt = $this->Prepare('insert into emp (empno, ename) values (:empno, :ename)');
786 function Prepare($sql,$cursor=false)
790 $stmt = OCIParse($this->_connectionID
,$sql);
793 $this->_errorMsg
= false;
794 $this->_errorCode
= false;
795 $arr = @OCIError
($this->_connectionID
);
796 if ($arr === false) return false;
798 $this->_errorMsg
= $arr['message'];
799 $this->_errorCode
= $arr['code'];
805 $sttype = @OCIStatementType
($stmt);
806 if ($sttype == 'BEGIN' ||
$sttype == 'DECLARE') {
807 return array($sql,$stmt,0,$BINDNUM, ($cursor) ?
OCINewCursor($this->_connectionID
) : false);
809 return array($sql,$stmt,0,$BINDNUM);
813 Call an oracle stored procedure and returns a cursor variable as a recordset.
814 Concept by Robert Tuttle robert@ud.com
817 Note: we return a cursor variable in :RS2
818 $rs = $db->ExecuteCursor("BEGIN adodb.open_tab(:RS2); END;",'RS2');
820 $rs = $db->ExecuteCursor(
821 "BEGIN :RS2 = adodb.getdata(:VAR1); END;",
823 array('VAR1' => 'Mr Bean'));
826 function &ExecuteCursor($sql,$cursorName='rs',$params=false)
828 if (is_array($sql)) $stmt = $sql;
829 else $stmt = ADODB_oci8
::Prepare($sql,true); # true to allocate OCINewCursor
831 if (is_array($stmt) && sizeof($stmt) >= 5) {
834 $this->Parameter($stmt, $ignoreCur, $cursorName, false, -1, OCI_B_CURSOR
);
836 foreach($params as $k => $v) {
837 $this->Parameter($stmt,$params[$k], $k);
843 $rs =& $this->Execute($stmt);
845 if ($rs->databaseType
== 'array') OCIFreeCursor($stmt[4]);
846 else if ($hasref) $rs->_refcursor
= $stmt[4];
852 Bind a variable -- very, very fast for executing repeated statements in oracle.
854 for ($i = 0; $i < $max; $i++) {
855 $p1 = ?; $p2 = ?; $p3 = ?;
856 $this->Execute("insert into table (col0, col1, col2) values (:0, :1, :2)",
861 $stmt = $DB->Prepare("insert into table (col0, col1, col2) values (:0, :1, :2)");
862 $DB->Bind($stmt, $p1);
863 $DB->Bind($stmt, $p2);
864 $DB->Bind($stmt, $p3);
865 for ($i = 0; $i < $max; $i++) {
866 $p1 = ?; $p2 = ?; $p3 = ?;
871 ** Test table has 3 cols, and 1 index. Test to insert 1000 records
872 Time 0.6081s (1644.60 inserts/sec) with direct OCIParse/OCIExecute
873 Time 0.6341s (1577.16 inserts/sec) with ADOdb Prepare/Bind/Execute
874 Time 1.5533s ( 643.77 inserts/sec) with pure SQL using Execute
876 Now if PHP only had batch/bulk updating like Java or PL/SQL...
878 Note that the order of parameters differs from OCIBindByName,
879 because we default the names to :0, :1, :2
881 function Bind(&$stmt,&$var,$size=4000,$type=false,$name=false,$isOutput=false)
884 if (!is_array($stmt)) return false;
886 if (($type == OCI_B_CURSOR
) && sizeof($stmt) >= 5) {
887 return OCIBindByName($stmt[1],":".$name,$stmt[4],$size,$type);
890 if ($name == false) {
891 if ($type !== false) $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size,$type);
892 else $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size); // +1 byte for null terminator
894 } else if (oci_lob_desc($type)) {
896 ADOConnection
::outp("<b>Bind</b>: name = $name");
898 //we have to create a new Descriptor here
899 $numlob = count($this->_refLOBs
);
900 $this->_refLOBs
[$numlob]['LOB'] = OCINewDescriptor($this->_connectionID
, oci_lob_desc($type));
901 $this->_refLOBs
[$numlob]['TYPE'] = $isOutput;
903 $tmp = &$this->_refLOBs
[$numlob]['LOB'];
904 $rez = OCIBindByName($stmt[1], ":".$name, $tmp, -1, $type);
906 ADOConnection
::outp("<b>Bind</b>: descriptor has been allocated, var (".$name.") binded");
909 // if type is input then write data to lob now
910 if ($isOutput == false) {
911 $var = $this->BlobEncode($var);
912 $tmp->WriteTemporary($var);
913 $this->_refLOBs
[$numlob]['VAR'] = &$var;
915 ADOConnection
::outp("<b>Bind</b>: LOB has been written to temp");
918 $this->_refLOBs
[$numlob]['VAR'] = &$var;
923 ADOConnection
::outp("<b>Bind</b>: name = $name");
925 if ($type !== false) $rez = OCIBindByName($stmt[1],":".$name,$var,$size,$type);
926 else $rez = OCIBindByName($stmt[1],":".$name,$var,$size); // +1 byte for null terminator
932 function Param($name,$type=false)
939 $stmt = $db->Prepare('select * from table where id =:myid and group=:group');
940 $db->Parameter($stmt,$id,'myid');
941 $db->Parameter($stmt,$group,'group');
944 @param $stmt Statement returned by Prepare() or PrepareSP().
945 @param $var PHP variable to bind to
946 @param $name Name of stored procedure variable name to bind to.
947 @param [$isOutput] Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8.
948 @param [$maxLen] Holds an maximum length of the variable.
949 @param [$type] The data type of $var. Legal values depend on driver.
951 See OCIBindByName documentation at php.net.
953 function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
956 $prefix = ($isOutput) ?
'Out' : 'In';
957 $ztype = (empty($type)) ?
'false' : $type;
958 ADOConnection
::outp( "{$prefix}Parameter(\$stmt, \$php_var='$var', \$name='$name', \$maxLen=$maxLen, \$type=$ztype);");
960 return $this->Bind($stmt,$var,$maxLen,$type,$name,$isOutput);
964 returns query ID if successful, otherwise false
965 this version supports:
967 1. $db->execute('select * from table');
969 2. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
970 $db->execute($prepared_statement, array(1,2,3));
972 3. $db->execute('insert into table (a,b,c) values (:a,:b,:c)',array('a'=>1,'b'=>2,'c'=>3));
974 4. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
975 $db->bind($stmt,1); $db->bind($stmt,2); $db->bind($stmt,3);
978 function _query($sql,$inputarr)
980 if (is_array($sql)) { // is prepared sql
983 // we try to bind to permanent array, so that OCIBindByName is persistent
984 // and carried out once only - note that max array element size is 4000 chars
985 if (is_array($inputarr)) {
987 if (isset($this->_bind
[$bindpos])) {
988 // all tied up already
989 $bindarr = &$this->_bind
[$bindpos];
991 // one statement to bind them all
993 foreach($inputarr as $k => $v) {
995 OCIBindByName($stmt,":$k",$bindarr[$k],is_string($v) && strlen($v)>4000 ?
-1 : 4000);
997 $this->_bind
[$bindpos] = &$bindarr;
1001 $stmt=OCIParse($this->_connectionID
,$sql);
1004 $this->_stmt
= $stmt;
1005 if (!$stmt) return false;
1007 if (defined('ADODB_PREFETCH_ROWS')) @OCISetPrefetch
($stmt,ADODB_PREFETCH_ROWS
);
1009 if (is_array($inputarr)) {
1010 foreach($inputarr as $k => $v) {
1012 if (sizeof($v) == 2) // suggested by g.giunta@libero.
1013 OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]);
1015 OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
1017 if ($this->debug
==99) echo "name=:$k",' var='.$inputarr[$k][0],' len='.$v[1],' type='.$v[2],'<br>';
1020 if ($v === ' ') $len = 1;
1021 if (isset($bindarr)) { // is prepared sql, so no need to ocibindbyname again
1023 } else { // dynamic sql, so rebind every time
1024 OCIBindByName($stmt,":$k",$inputarr[$k],$len);
1030 $this->_errorMsg
= false;
1031 $this->_errorCode
= false;
1032 if (OCIExecute($stmt,$this->_commit
)) {
1033 //OCIInternalDebug(1);
1034 if (count($this -> _refLOBs
) > 0) {
1036 foreach ($this -> _refLOBs
as $key => $value) {
1037 if ($this -> _refLOBs
[$key]['TYPE'] == true) {
1038 $tmp = $this -> _refLOBs
[$key]['LOB'] -> load();
1039 if ($this -> debug
) {
1040 ADOConnection
::outp("<b>OUT LOB</b>: LOB has been loaded. <br>");
1042 //$_GLOBALS[$this -> _refLOBs[$key]['VAR']] = $tmp;
1043 $this -> _refLOBs
[$key]['VAR'] = $tmp;
1045 $this->_refLOBs
[$key]['LOB']->save($this->_refLOBs
[$key]['VAR']);
1046 $this -> _refLOBs
[$key]['LOB']->free();
1047 unset($this -> _refLOBs
[$key]);
1049 ADOConnection
::outp("<b>IN LOB</b>: LOB has been saved. <br>");
1055 switch (@OCIStatementType
($stmt)) {
1061 if (is_array($sql) && !empty($sql[4])) {
1063 if (is_resource($cursor)) {
1064 $ok = OCIExecute($cursor);
1069 if (is_resource($stmt)) {
1070 OCIFreeStatement($stmt);
1077 // ociclose -- no because it could be used in a LOB?
1084 // returns true or false
1087 if (!$this->_connectionID
) return;
1089 if (!$this->autoCommit
) OCIRollback($this->_connectionID
);
1090 if (count($this->_refLOBs
) > 0) {
1091 foreach ($this ->_refLOBs
as $key => $value) {
1092 $this->_refLOBs
[$key]['LOB']->free();
1093 unset($this->_refLOBs
[$key]);
1096 OCILogoff($this->_connectionID
);
1098 $this->_stmt
= false;
1099 $this->_connectionID
= false;
1102 function MetaPrimaryKeys($table, $owner=false,$internalKey=false)
1104 if ($internalKey) return array('ROWID');
1106 // tested with oracle 8.1.7
1107 $table = strtoupper($table);
1109 $owner_clause = "AND ((a.OWNER = b.OWNER) AND (a.OWNER = UPPER('$owner')))";
1116 SELECT /*+ RULE */ distinct b.column_name
1117 FROM {$ptab}CONSTRAINTS a
1118 , {$ptab}CONS_COLUMNS b
1119 WHERE ( UPPER(b.table_name) = ('$table'))
1120 AND (UPPER(a.table_name) = ('$table') and a.constraint_type = 'P')
1122 AND (a.constraint_name = b.constraint_name)";
1124 $rs = $this->Execute($sql);
1125 if ($rs && !$rs->EOF
) {
1126 $arr =& $rs->GetArray();
1128 foreach($arr as $v) {
1136 // http://gis.mit.edu/classes/11.521/sqlnotes/referential_integrity.html
1137 function MetaForeignKeys($table, $owner=false)
1139 global $ADODB_FETCH_MODE;
1141 $save = $ADODB_FETCH_MODE;
1142 $ADODB_FETCH_MODE = ADODB_FETCH_NUM
;
1143 $table = $this->qstr(strtoupper($table));
1145 $owner = $this->user
;
1150 $owner = ' and owner='.$this->qstr(strtoupper($owner));
1153 "select constraint_name,r_owner,r_constraint_name
1154 from {$tabp}constraints
1155 where constraint_type = 'R' and table_name = $table $owner";
1157 $constraints =& $this->GetArray($sql);
1159 foreach($constraints as $constr) {
1160 $cons = $this->qstr($constr[0]);
1161 $rowner = $this->qstr($constr[1]);
1162 $rcons = $this->qstr($constr[2]);
1163 $cols = $this->GetArray("select column_name from {$tabp}cons_columns where constraint_name=$cons $owner order by position");
1164 $tabcol = $this->GetArray("select table_name,column_name from {$tabp}cons_columns where owner=$rowner and constraint_name=$rcons order by position");
1166 if ($cols && $tabcol)
1167 for ($i=0, $max=sizeof($cols); $i < $max; $i++
) {
1168 $arr[$tabcol[$i][0]] = $cols[$i][0].'='.$tabcol[$i][1];
1171 $ADODB_FETCH_MODE = $save;
1189 * An example is $db->qstr("Don't bother",magic_quotes_runtime());
1191 * @param s the string to quote
1192 * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc().
1193 * This undoes the stupidity of magic quotes for GPC.
1195 * @return quoted string to be sent back to database
1197 function qstr($s,$magic_quotes=false)
1199 //$nofixquotes=false;
1201 if ($this->noNullStrings
&& strlen($s)==0)$s = ' ';
1202 if (!$magic_quotes) {
1203 if ($this->replaceQuote
[0] == '\\'){
1204 $s = str_replace('\\','\\\\',$s);
1206 return "'".str_replace("'",$this->replaceQuote
,$s)."'";
1209 // undo magic quotes for "
1210 $s = str_replace('\\"','"',$s);
1212 $s = str_replace('\\\\','\\',$s);
1213 return "'".str_replace("\\'",$this->replaceQuote
,$s)."'";
1219 /*--------------------------------------------------------------------------------------
1220 Class Name: Recordset
1221 --------------------------------------------------------------------------------------*/
1223 class ADORecordset_oci8
extends ADORecordSet
{
1225 var $databaseType = 'oci8';
1229 //var $_arr = false;
1231 function ADORecordset_oci8($queryID,$mode=false)
1233 if ($mode === false) {
1234 global $ADODB_FETCH_MODE;
1235 $mode = $ADODB_FETCH_MODE;
1239 case ADODB_FETCH_ASSOC
:$this->fetchMode
= OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS
; break;
1240 case ADODB_FETCH_DEFAULT
:
1241 case ADODB_FETCH_BOTH
:$this->fetchMode
= OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS
; break;
1242 case ADODB_FETCH_NUM
:
1244 $this->fetchMode
= OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS
; break;
1247 $this->adodbFetchMode
= $mode;
1248 $this->_queryID
= $queryID;
1254 if ($this->_inited
) return;
1256 $this->_inited
= true;
1257 if ($this->_queryID
) {
1259 $this->_currentRow
= 0;
1261 $this->EOF
= !$this->_fetch();
1264 // based on idea by Gaetano Giunta to detect unusual oracle errors
1265 // see http://phplens.com/lens/lensforum/msgs.php?id=6771
1266 $err = OCIError($this->_queryID);
1267 if ($err && $this->connection->debug) ADOConnection::outp($err);
1270 if (!is_array($this->fields
)) {
1271 $this->_numOfRows
= 0;
1272 $this->fields
= array();
1275 $this->fields
= array();
1276 $this->_numOfRows
= 0;
1277 $this->_numOfFields
= 0;
1284 $this->_numOfRows
= -1;
1285 $this->_numOfFields
= OCInumcols($this->_queryID
);
1286 if ($this->_numOfFields
>0) {
1287 $this->_fieldobjs
= array();
1288 $max = $this->_numOfFields
;
1289 for ($i=0;$i<$max; $i++
) $this->_fieldobjs
[] = $this->_FetchField($i);
1293 /* Returns: an object containing field information.
1294 Get column information in the Recordset object. fetchField() can be used in order to obtain information about
1295 fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
1296 fetchField() is retrieved. */
1298 function _FetchField($fieldOffset = -1)
1300 $fld = new ADOFieldObject
;
1302 $fld->name
=OCIcolumnname($this->_queryID
, $fieldOffset);
1303 $fld->type
= OCIcolumntype($this->_queryID
, $fieldOffset);
1304 $fld->max_length
= OCIcolumnsize($this->_queryID
, $fieldOffset);
1305 switch($fld->type
) {
1307 $p = OCIColumnPrecision($this->_queryID
, $fieldOffset);
1308 $sc = OCIColumnScale($this->_queryID
, $fieldOffset);
1309 if ($p != 0 && $sc == 0) $fld->type
= 'INT';
1315 $fld->max_length
= -1;
1321 /* For some reason, OCIcolumnname fails when called after _initrs() so we cache it */
1322 function &FetchField($fieldOffset = -1)
1324 return $this->_fieldobjs
[$fieldOffset];
1329 // 10% speedup to move MoveNext to child class
1330 function _MoveNext()
1332 //global $ADODB_EXTENSION;if ($ADODB_EXTENSION) return @adodb_movenext($this);
1334 if ($this->EOF) return false;
1336 $this->_currentRow++;
1337 if(@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode))
1347 if (@OCIfetchinto
($this->_queryID
,$this->fields
,$this->fetchMode
)) {
1348 $this->_currentRow +
= 1;
1352 $this->_currentRow +
= 1;
1359 # does not work as first record is retrieved in _initrs(), so is not included in GetArray()
1360 function &GetArray($nRows = -1)
1362 global $ADODB_OCI8_GETARRAY;
1364 if (true || !empty($ADODB_OCI8_GETARRAY)) {
1365 # does not support $ADODB_ANSI_PADDING_OFF
1367 //OCI_RETURN_NULLS and OCI_RETURN_LOBS is set by OCIfetchstatement
1368 switch($this->adodbFetchMode) {
1369 case ADODB_FETCH_NUM:
1371 $ncols = @OCIfetchstatement($this->_queryID, $results, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW+OCI_NUM);
1372 $results = array_merge(array($this->fields),$results);
1375 case ADODB_FETCH_ASSOC:
1376 if (ADODB_ASSOC_CASE != 2 || $this->databaseType != 'oci8') break;
1378 $ncols = @OCIfetchstatement($this->_queryID, $assoc, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW);
1379 $results =& array_merge(array($this->fields),$assoc);
1387 $results =& ADORecordSet::GetArray($nRows);
1392 /* Optimize SelectLimit() by using OCIFetch() instead of OCIFetchInto() */
1393 function &GetArrayLimit($nrows,$offset=-1)
1396 $arr =& $this->GetArray($nrows);
1400 for ($i=1; $i < $offset; $i++
)
1401 if (!@OCIFetch
($this->_queryID
)) return $arr;
1403 if (!@OCIfetchinto
($this->_queryID
,$this->fields
,$this->fetchMode
)) return $arr;;
1406 while (!$this->EOF
&& $nrows != $cnt) {
1407 $results[$cnt++
] = $this->fields
;
1415 /* Use associative array to get fields array */
1416 function Fields($colname)
1419 $this->bind
= array();
1420 for ($i=0; $i < $this->_numOfFields
; $i++
) {
1421 $o = $this->FetchField($i);
1422 $this->bind
[strtoupper($o->name
)] = $i;
1426 return $this->fields
[$this->bind
[strtoupper($colname)]];
1431 function _seek($row)
1438 return @OCIfetchinto
($this->_queryID
,$this->fields
,$this->fetchMode
);
1441 /* close() only needs to be called if you are worried about using too much memory while your script
1442 is running. All associated result memory for the specified result identifier will automatically be freed. */
1446 if ($this->connection
->_stmt
=== $this->_queryID
) $this->connection
->_stmt
= false;
1447 if (!empty($this->_refcursor
)) {
1448 OCIFreeCursor($this->_refcursor
);
1449 $this->_refcursor
= false;
1451 @OCIFreeStatement
($this->_queryID
);
1452 $this->_queryID
= false;
1456 function MetaType($t,$len=-1)
1458 if (is_object($t)) {
1460 $t = $fieldobj->type
;
1461 $len = $fieldobj->max_length
;
1463 switch (strtoupper($t)) {
1472 if (isset($this) && $len <= $this->blobSize
) return 'C';
1476 case 'LONG VARCHAR':
1481 case 'LONG VARBINARY':
1486 return ($this->connection
->datetime
) ?
'T' : 'D';
1489 case 'TIMESTAMP': return 'T';
1496 default: return 'N';
1501 class ADORecordSet_ext_oci8
extends ADORecordSet_oci8
{
1502 function ADORecordSet_ext_oci8($queryID,$mode=false)
1504 if ($mode === false) {
1505 global $ADODB_FETCH_MODE;
1506 $mode = $ADODB_FETCH_MODE;
1510 case ADODB_FETCH_ASSOC
:$this->fetchMode
= OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS
; break;
1511 case ADODB_FETCH_DEFAULT
:
1512 case ADODB_FETCH_BOTH
:$this->fetchMode
= OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS
; break;
1513 case ADODB_FETCH_NUM
:
1514 default: $this->fetchMode
= OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS
; break;
1516 $this->adodbFetchMode
= $mode;
1517 $this->_queryID
= $queryID;
1522 return adodb_movenext($this);