4 version V4.94 23 Jan 2007 (c) 2000-2007 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.'"';
546 This algorithm makes use of
549 The FIRST_ROWS hint explicitly chooses the approach to optimize response time,
550 that is, minimum resource usage to return the first row. Results will be returned
551 as soon as they are identified.
553 b. Uses rownum tricks to obtain only the required rows from a given offset.
554 As this uses complicated sql statements, we only use this if the $offset >= 100.
555 This idea by Tomas V V Cox.
557 This implementation does not appear to work with oracle 8.0.5 or earlier. Comment
558 out this function then, and the slower SelectLimit() in the base class will be used.
560 function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
562 // seems that oracle only supports 1 hint comment in 8i
563 if ($this->firstrows
) {
564 if (strpos($sql,'/*+') !== false)
565 $sql = str_replace('/*+ ','/*+FIRST_ROWS ',$sql);
567 $sql = preg_replace('/^[ \t\n]*select/i','SELECT /*+FIRST_ROWS*/',$sql);
570 if ($offset < $this->selectOffsetAlg1
) {
572 if ($offset > 0) $nrows +
= $offset;
573 //$inputarr['adodb_rownum'] = $nrows;
574 if ($this->databaseType
== 'oci8po') {
575 $sql = "select * from (".$sql.") where rownum <= ?";
577 $sql = "select * from (".$sql.") where rownum <= :adodb_offset";
579 $inputarr['adodb_offset'] = $nrows;
582 // note that $nrows = 0 still has to work ==> no rows returned
584 $rs =& ADOConnection
::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
588 // Algorithm by Tomas V V Cox, from PEAR DB oci8.php
590 // Let Oracle return the name of the columns
591 $q_fields = "SELECT * FROM (".$sql.") WHERE NULL = NULL";
594 if (! $stmt_arr = $this->Prepare($q_fields)) {
597 $stmt = $stmt_arr[1];
599 if (is_array($inputarr)) {
600 foreach($inputarr as $k => $v) {
602 if (sizeof($v) == 2) // suggested by g.giunta@libero.
603 OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]);
605 OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
608 if ($v === ' ') $len = 1;
609 if (isset($bindarr)) { // is prepared sql, so no need to ocibindbyname again
611 } else { // dynamic sql, so rebind every time
612 OCIBindByName($stmt,":$k",$inputarr[$k],$len);
618 if (!OCIExecute($stmt, OCI_DEFAULT
)) {
619 OCIFreeStatement($stmt);
623 $ncols = OCINumCols($stmt);
624 for ( $i = 1; $i <= $ncols; $i++
) {
625 $cols[] = '"'.OCIColumnName($stmt, $i).'"';
629 OCIFreeStatement($stmt);
630 $fields = implode(',', $cols);
632 $offset +
= 1; // in Oracle rownum starts at 1
634 if ($this->databaseType
== 'oci8po') {
635 $sql = "SELECT $fields FROM".
636 "(SELECT rownum as adodb_rownum, $fields FROM".
637 " ($sql) WHERE rownum <= ?".
638 ") WHERE adodb_rownum >= ?";
640 $sql = "SELECT $fields FROM".
641 "(SELECT rownum as adodb_rownum, $fields FROM".
642 " ($sql) WHERE rownum <= :adodb_nrows".
643 ") WHERE adodb_rownum >= :adodb_offset";
645 $inputarr['adodb_nrows'] = $nrows;
646 $inputarr['adodb_offset'] = $offset;
648 if ($secs2cache>0) $rs =& $this->CacheExecute($secs2cache, $sql,$inputarr);
649 else $rs =& $this->Execute($sql,$inputarr);
657 * Store BLOBs and CLOBs
659 * Example: to store $var in a blob
661 * $conn->Execute('insert into TABLE (id,ablob) values(12,empty_blob())');
662 * $conn->UpdateBlob('TABLE', 'ablob', $varHoldingBlob, 'ID=12', 'BLOB');
664 * $blobtype supports 'BLOB' and 'CLOB', but you need to change to 'empty_clob()'.
666 * to get length of LOB:
667 * select DBMS_LOB.GETLENGTH(ablob) from TABLE
669 * If you are using CURSOR_SHARING = force, it appears this will case a segfault
670 * under oracle 8.1.7.0. Run:
671 * $db->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT');
672 * before UpdateBlob() then...
675 function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
678 //if (strlen($val) < 4000) return $this->Execute("UPDATE $table SET $column=:blob WHERE $where",array('blob'=>$val)) != false;
680 switch(strtoupper($blobtype)) {
681 default: ADOConnection
::outp("<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
682 case 'BLOB': $type = OCI_B_BLOB
; break;
683 case 'CLOB': $type = OCI_B_CLOB
; break;
686 if ($this->databaseType
== 'oci8po')
687 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
689 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
691 $desc = OCINewDescriptor($this->_connectionID
, OCI_D_LOB
);
692 $arr['blob'] = array($desc,-1,$type);
693 if ($this->session_sharing_force_blob
) $this->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT');
694 $commit = $this->autoCommit
;
695 if ($commit) $this->BeginTrans();
696 $rs = $this->_Execute($sql,$arr);
697 if ($rez = !empty($rs)) $desc->save($val);
699 if ($commit) $this->CommitTrans();
700 if ($this->session_sharing_force_blob
) $this->Execute('ALTER SESSION SET CURSOR_SHARING=FORCE');
702 if ($rez) $rs->Close();
707 * Usage: store file pointed to by $var in a blob
709 function UpdateBlobFile($table,$column,$val,$where,$blobtype='BLOB')
711 switch(strtoupper($blobtype)) {
712 default: ADOConnection
::outp( "<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
713 case 'BLOB': $type = OCI_B_BLOB
; break;
714 case 'CLOB': $type = OCI_B_CLOB
; break;
717 if ($this->databaseType
== 'oci8po')
718 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
720 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
722 $desc = OCINewDescriptor($this->_connectionID
, OCI_D_LOB
);
723 $arr['blob'] = array($desc,-1,$type);
726 $rs = ADODB_oci8
::Execute($sql,$arr);
727 if ($rez = !empty($rs)) $desc->savefile($val);
729 $this->CommitTrans();
731 if ($rez) $rs->Close();
738 * @param sql SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text)
739 * @param [inputarr] holds the input data to bind to. Null elements will be set to null.
740 * @return RecordSet or false
742 function &Execute($sql,$inputarr=false)
744 if ($this->fnExecute
) {
745 $fn = $this->fnExecute
;
746 $ret =& $fn($this,$sql,$inputarr);
747 if (isset($ret)) return $ret;
750 #if (!is_array($inputarr)) $inputarr = array($inputarr);
752 $element0 = reset($inputarr);
754 # is_object check because oci8 descriptors can be passed in
755 if (is_array($element0) && !is_object(reset($element0))) {
757 $stmt = $this->Prepare($sql);
761 foreach($inputarr as $arr) {
762 $ret =& $this->_Execute($stmt,$arr);
763 if (!$ret) return $ret;
766 $ret =& $this->_Execute($sql,$inputarr);
770 $ret =& $this->_Execute($sql,false);
779 $stmt = $this->Prepare('insert into emp (empno, ename) values (:empno, :ename)');
781 function Prepare($sql,$cursor=false)
785 $stmt = OCIParse($this->_connectionID
,$sql);
788 $this->_errorMsg
= false;
789 $this->_errorCode
= false;
790 $arr = @OCIError
($this->_connectionID
);
791 if ($arr === false) return false;
793 $this->_errorMsg
= $arr['message'];
794 $this->_errorCode
= $arr['code'];
800 $sttype = @OCIStatementType
($stmt);
801 if ($sttype == 'BEGIN' ||
$sttype == 'DECLARE') {
802 return array($sql,$stmt,0,$BINDNUM, ($cursor) ?
OCINewCursor($this->_connectionID
) : false);
804 return array($sql,$stmt,0,$BINDNUM);
808 Call an oracle stored procedure and returns a cursor variable as a recordset.
809 Concept by Robert Tuttle robert@ud.com
812 Note: we return a cursor variable in :RS2
813 $rs = $db->ExecuteCursor("BEGIN adodb.open_tab(:RS2); END;",'RS2');
815 $rs = $db->ExecuteCursor(
816 "BEGIN :RS2 = adodb.getdata(:VAR1); END;",
818 array('VAR1' => 'Mr Bean'));
821 function &ExecuteCursor($sql,$cursorName='rs',$params=false)
823 if (is_array($sql)) $stmt = $sql;
824 else $stmt = ADODB_oci8
::Prepare($sql,true); # true to allocate OCINewCursor
826 if (is_array($stmt) && sizeof($stmt) >= 5) {
829 $this->Parameter($stmt, $ignoreCur, $cursorName, false, -1, OCI_B_CURSOR
);
831 foreach($params as $k => $v) {
832 $this->Parameter($stmt,$params[$k], $k);
838 $rs =& $this->Execute($stmt);
840 if ($rs->databaseType
== 'array') OCIFreeCursor($stmt[4]);
841 else if ($hasref) $rs->_refcursor
= $stmt[4];
847 Bind a variable -- very, very fast for executing repeated statements in oracle.
849 for ($i = 0; $i < $max; $i++) {
850 $p1 = ?; $p2 = ?; $p3 = ?;
851 $this->Execute("insert into table (col0, col1, col2) values (:0, :1, :2)",
856 $stmt = $DB->Prepare("insert into table (col0, col1, col2) values (:0, :1, :2)");
857 $DB->Bind($stmt, $p1);
858 $DB->Bind($stmt, $p2);
859 $DB->Bind($stmt, $p3);
860 for ($i = 0; $i < $max; $i++) {
861 $p1 = ?; $p2 = ?; $p3 = ?;
866 ** Test table has 3 cols, and 1 index. Test to insert 1000 records
867 Time 0.6081s (1644.60 inserts/sec) with direct OCIParse/OCIExecute
868 Time 0.6341s (1577.16 inserts/sec) with ADOdb Prepare/Bind/Execute
869 Time 1.5533s ( 643.77 inserts/sec) with pure SQL using Execute
871 Now if PHP only had batch/bulk updating like Java or PL/SQL...
873 Note that the order of parameters differs from OCIBindByName,
874 because we default the names to :0, :1, :2
876 function Bind(&$stmt,&$var,$size=4000,$type=false,$name=false,$isOutput=false)
879 if (!is_array($stmt)) return false;
881 if (($type == OCI_B_CURSOR
) && sizeof($stmt) >= 5) {
882 return OCIBindByName($stmt[1],":".$name,$stmt[4],$size,$type);
885 if ($name == false) {
886 if ($type !== false) $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size,$type);
887 else $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size); // +1 byte for null terminator
889 } else if (oci_lob_desc($type)) {
891 ADOConnection
::outp("<b>Bind</b>: name = $name");
893 //we have to create a new Descriptor here
894 $numlob = count($this->_refLOBs
);
895 $this->_refLOBs
[$numlob]['LOB'] = OCINewDescriptor($this->_connectionID
, oci_lob_desc($type));
896 $this->_refLOBs
[$numlob]['TYPE'] = $isOutput;
898 $tmp = &$this->_refLOBs
[$numlob]['LOB'];
899 $rez = OCIBindByName($stmt[1], ":".$name, $tmp, -1, $type);
901 ADOConnection
::outp("<b>Bind</b>: descriptor has been allocated, var (".$name.") binded");
904 // if type is input then write data to lob now
905 if ($isOutput == false) {
906 $var = $this->BlobEncode($var);
907 $tmp->WriteTemporary($var);
908 $this->_refLOBs
[$numlob]['VAR'] = &$var;
910 ADOConnection
::outp("<b>Bind</b>: LOB has been written to temp");
913 $this->_refLOBs
[$numlob]['VAR'] = &$var;
918 ADOConnection
::outp("<b>Bind</b>: name = $name");
920 if ($type !== false) $rez = OCIBindByName($stmt[1],":".$name,$var,$size,$type);
921 else $rez = OCIBindByName($stmt[1],":".$name,$var,$size); // +1 byte for null terminator
927 function Param($name,$type=false)
934 $stmt = $db->Prepare('select * from table where id =:myid and group=:group');
935 $db->Parameter($stmt,$id,'myid');
936 $db->Parameter($stmt,$group,'group');
939 @param $stmt Statement returned by Prepare() or PrepareSP().
940 @param $var PHP variable to bind to
941 @param $name Name of stored procedure variable name to bind to.
942 @param [$isOutput] Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8.
943 @param [$maxLen] Holds an maximum length of the variable.
944 @param [$type] The data type of $var. Legal values depend on driver.
946 See OCIBindByName documentation at php.net.
948 function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
951 $prefix = ($isOutput) ?
'Out' : 'In';
952 $ztype = (empty($type)) ?
'false' : $type;
953 ADOConnection
::outp( "{$prefix}Parameter(\$stmt, \$php_var='$var', \$name='$name', \$maxLen=$maxLen, \$type=$ztype);");
955 return $this->Bind($stmt,$var,$maxLen,$type,$name,$isOutput);
959 returns query ID if successful, otherwise false
960 this version supports:
962 1. $db->execute('select * from table');
964 2. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
965 $db->execute($prepared_statement, array(1,2,3));
967 3. $db->execute('insert into table (a,b,c) values (:a,:b,:c)',array('a'=>1,'b'=>2,'c'=>3));
969 4. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
970 $db->bind($stmt,1); $db->bind($stmt,2); $db->bind($stmt,3);
973 function _query($sql,$inputarr)
975 if (is_array($sql)) { // is prepared sql
978 // we try to bind to permanent array, so that OCIBindByName is persistent
979 // and carried out once only - note that max array element size is 4000 chars
980 if (is_array($inputarr)) {
982 if (isset($this->_bind
[$bindpos])) {
983 // all tied up already
984 $bindarr = &$this->_bind
[$bindpos];
986 // one statement to bind them all
988 foreach($inputarr as $k => $v) {
990 OCIBindByName($stmt,":$k",$bindarr[$k],is_string($v) && strlen($v)>4000 ?
-1 : 4000);
992 $this->_bind
[$bindpos] = &$bindarr;
996 $stmt=OCIParse($this->_connectionID
,$sql);
999 $this->_stmt
= $stmt;
1000 if (!$stmt) return false;
1002 if (defined('ADODB_PREFETCH_ROWS')) @OCISetPrefetch
($stmt,ADODB_PREFETCH_ROWS
);
1004 if (is_array($inputarr)) {
1005 foreach($inputarr as $k => $v) {
1007 if (sizeof($v) == 2) // suggested by g.giunta@libero.
1008 OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]);
1010 OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
1012 if ($this->debug
==99) echo "name=:$k",' var='.$inputarr[$k][0],' len='.$v[1],' type='.$v[2],'<br>';
1015 if ($v === ' ') $len = 1;
1016 if (isset($bindarr)) { // is prepared sql, so no need to ocibindbyname again
1018 } else { // dynamic sql, so rebind every time
1019 OCIBindByName($stmt,":$k",$inputarr[$k],$len);
1025 $this->_errorMsg
= false;
1026 $this->_errorCode
= false;
1027 if (OCIExecute($stmt,$this->_commit
)) {
1028 //OCIInternalDebug(1);
1029 if (count($this -> _refLOBs
) > 0) {
1031 foreach ($this -> _refLOBs
as $key => $value) {
1032 if ($this -> _refLOBs
[$key]['TYPE'] == true) {
1033 $tmp = $this -> _refLOBs
[$key]['LOB'] -> load();
1034 if ($this -> debug
) {
1035 ADOConnection
::outp("<b>OUT LOB</b>: LOB has been loaded. <br>");
1037 //$_GLOBALS[$this -> _refLOBs[$key]['VAR']] = $tmp;
1038 $this -> _refLOBs
[$key]['VAR'] = $tmp;
1040 $this->_refLOBs
[$key]['LOB']->save($this->_refLOBs
[$key]['VAR']);
1041 $this -> _refLOBs
[$key]['LOB']->free();
1042 unset($this -> _refLOBs
[$key]);
1044 ADOConnection
::outp("<b>IN LOB</b>: LOB has been saved. <br>");
1050 switch (@OCIStatementType
($stmt)) {
1056 if (is_array($sql) && !empty($sql[4])) {
1058 if (is_resource($cursor)) {
1059 $ok = OCIExecute($cursor);
1064 if (is_resource($stmt)) {
1065 OCIFreeStatement($stmt);
1072 // ociclose -- no because it could be used in a LOB?
1079 // returns true or false
1082 if (!$this->_connectionID
) return;
1084 if (!$this->autoCommit
) OCIRollback($this->_connectionID
);
1085 if (count($this->_refLOBs
) > 0) {
1086 foreach ($this ->_refLOBs
as $key => $value) {
1087 $this->_refLOBs
[$key]['LOB']->free();
1088 unset($this->_refLOBs
[$key]);
1091 OCILogoff($this->_connectionID
);
1093 $this->_stmt
= false;
1094 $this->_connectionID
= false;
1097 function MetaPrimaryKeys($table, $owner=false,$internalKey=false)
1099 if ($internalKey) return array('ROWID');
1101 // tested with oracle 8.1.7
1102 $table = strtoupper($table);
1104 $owner_clause = "AND ((a.OWNER = b.OWNER) AND (a.OWNER = UPPER('$owner')))";
1111 SELECT /*+ RULE */ distinct b.column_name
1112 FROM {$ptab}CONSTRAINTS a
1113 , {$ptab}CONS_COLUMNS b
1114 WHERE ( UPPER(b.table_name) = ('$table'))
1115 AND (UPPER(a.table_name) = ('$table') and a.constraint_type = 'P')
1117 AND (a.constraint_name = b.constraint_name)";
1119 $rs = $this->Execute($sql);
1120 if ($rs && !$rs->EOF
) {
1121 $arr =& $rs->GetArray();
1123 foreach($arr as $v) {
1131 // http://gis.mit.edu/classes/11.521/sqlnotes/referential_integrity.html
1132 function MetaForeignKeys($table, $owner=false)
1134 global $ADODB_FETCH_MODE;
1136 $save = $ADODB_FETCH_MODE;
1137 $ADODB_FETCH_MODE = ADODB_FETCH_NUM
;
1138 $table = $this->qstr(strtoupper($table));
1140 $owner = $this->user
;
1145 $owner = ' and owner='.$this->qstr(strtoupper($owner));
1148 "select constraint_name,r_owner,r_constraint_name
1149 from {$tabp}constraints
1150 where constraint_type = 'R' and table_name = $table $owner";
1152 $constraints =& $this->GetArray($sql);
1154 foreach($constraints as $constr) {
1155 $cons = $this->qstr($constr[0]);
1156 $rowner = $this->qstr($constr[1]);
1157 $rcons = $this->qstr($constr[2]);
1158 $cols = $this->GetArray("select column_name from {$tabp}cons_columns where constraint_name=$cons $owner order by position");
1159 $tabcol = $this->GetArray("select table_name,column_name from {$tabp}cons_columns where owner=$rowner and constraint_name=$rcons order by position");
1161 if ($cols && $tabcol)
1162 for ($i=0, $max=sizeof($cols); $i < $max; $i++
) {
1163 $arr[$tabcol[$i][0]] = $cols[$i][0].'='.$tabcol[$i][1];
1166 $ADODB_FETCH_MODE = $save;
1184 * An example is $db->qstr("Don't bother",magic_quotes_runtime());
1186 * @param s the string to quote
1187 * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc().
1188 * This undoes the stupidity of magic quotes for GPC.
1190 * @return quoted string to be sent back to database
1192 function qstr($s,$magic_quotes=false)
1194 //$nofixquotes=false;
1196 if ($this->noNullStrings
&& strlen($s)==0)$s = ' ';
1197 if (!$magic_quotes) {
1198 if ($this->replaceQuote
[0] == '\\'){
1199 $s = str_replace('\\','\\\\',$s);
1201 return "'".str_replace("'",$this->replaceQuote
,$s)."'";
1204 // undo magic quotes for "
1205 $s = str_replace('\\"','"',$s);
1207 $s = str_replace('\\\\','\\',$s);
1208 return "'".str_replace("\\'",$this->replaceQuote
,$s)."'";
1214 /*--------------------------------------------------------------------------------------
1215 Class Name: Recordset
1216 --------------------------------------------------------------------------------------*/
1218 class ADORecordset_oci8
extends ADORecordSet
{
1220 var $databaseType = 'oci8';
1224 //var $_arr = false;
1226 function ADORecordset_oci8($queryID,$mode=false)
1228 if ($mode === false) {
1229 global $ADODB_FETCH_MODE;
1230 $mode = $ADODB_FETCH_MODE;
1234 case ADODB_FETCH_ASSOC
:$this->fetchMode
= OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS
; break;
1235 case ADODB_FETCH_DEFAULT
:
1236 case ADODB_FETCH_BOTH
:$this->fetchMode
= OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS
; break;
1237 case ADODB_FETCH_NUM
:
1239 $this->fetchMode
= OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS
; break;
1242 $this->adodbFetchMode
= $mode;
1243 $this->_queryID
= $queryID;
1249 if ($this->_inited
) return;
1251 $this->_inited
= true;
1252 if ($this->_queryID
) {
1254 $this->_currentRow
= 0;
1256 $this->EOF
= !$this->_fetch();
1259 // based on idea by Gaetano Giunta to detect unusual oracle errors
1260 // see http://phplens.com/lens/lensforum/msgs.php?id=6771
1261 $err = OCIError($this->_queryID);
1262 if ($err && $this->connection->debug) ADOConnection::outp($err);
1265 if (!is_array($this->fields
)) {
1266 $this->_numOfRows
= 0;
1267 $this->fields
= array();
1270 $this->fields
= array();
1271 $this->_numOfRows
= 0;
1272 $this->_numOfFields
= 0;
1279 $this->_numOfRows
= -1;
1280 $this->_numOfFields
= OCInumcols($this->_queryID
);
1281 if ($this->_numOfFields
>0) {
1282 $this->_fieldobjs
= array();
1283 $max = $this->_numOfFields
;
1284 for ($i=0;$i<$max; $i++
) $this->_fieldobjs
[] = $this->_FetchField($i);
1288 /* Returns: an object containing field information.
1289 Get column information in the Recordset object. fetchField() can be used in order to obtain information about
1290 fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
1291 fetchField() is retrieved. */
1293 function &_FetchField($fieldOffset = -1)
1295 $fld = new ADOFieldObject
;
1297 $fld->name
=OCIcolumnname($this->_queryID
, $fieldOffset);
1298 $fld->type
= OCIcolumntype($this->_queryID
, $fieldOffset);
1299 $fld->max_length
= OCIcolumnsize($this->_queryID
, $fieldOffset);
1300 if ($fld->type
== 'NUMBER') {
1301 $p = OCIColumnPrecision($this->_queryID
, $fieldOffset);
1302 $sc = OCIColumnScale($this->_queryID
, $fieldOffset);
1303 if ($p != 0 && $sc == 0) $fld->type
= 'INT';
1304 //echo " $this->name ($p.$sc) ";
1309 /* For some reason, OCIcolumnname fails when called after _initrs() so we cache it */
1310 function &FetchField($fieldOffset = -1)
1312 return $this->_fieldobjs
[$fieldOffset];
1317 // 10% speedup to move MoveNext to child class
1318 function _MoveNext()
1320 //global $ADODB_EXTENSION;if ($ADODB_EXTENSION) return @adodb_movenext($this);
1322 if ($this->EOF) return false;
1324 $this->_currentRow++;
1325 if(@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode))
1335 if (@OCIfetchinto
($this->_queryID
,$this->fields
,$this->fetchMode
)) {
1336 $this->_currentRow +
= 1;
1340 $this->_currentRow +
= 1;
1347 # does not work as first record is retrieved in _initrs(), so is not included in GetArray()
1348 function &GetArray($nRows = -1)
1350 global $ADODB_OCI8_GETARRAY;
1352 if (true || !empty($ADODB_OCI8_GETARRAY)) {
1353 # does not support $ADODB_ANSI_PADDING_OFF
1355 //OCI_RETURN_NULLS and OCI_RETURN_LOBS is set by OCIfetchstatement
1356 switch($this->adodbFetchMode) {
1357 case ADODB_FETCH_NUM:
1359 $ncols = @OCIfetchstatement($this->_queryID, $results, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW+OCI_NUM);
1360 $results = array_merge(array($this->fields),$results);
1363 case ADODB_FETCH_ASSOC:
1364 if (ADODB_ASSOC_CASE != 2 || $this->databaseType != 'oci8') break;
1366 $ncols = @OCIfetchstatement($this->_queryID, $assoc, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW);
1367 $results =& array_merge(array($this->fields),$assoc);
1375 $results =& ADORecordSet::GetArray($nRows);
1380 /* Optimize SelectLimit() by using OCIFetch() instead of OCIFetchInto() */
1381 function &GetArrayLimit($nrows,$offset=-1)
1384 $arr =& $this->GetArray($nrows);
1388 for ($i=1; $i < $offset; $i++
)
1389 if (!@OCIFetch
($this->_queryID
)) return $arr;
1391 if (!@OCIfetchinto
($this->_queryID
,$this->fields
,$this->fetchMode
)) return $arr;;
1394 while (!$this->EOF
&& $nrows != $cnt) {
1395 $results[$cnt++
] = $this->fields
;
1403 /* Use associative array to get fields array */
1404 function Fields($colname)
1407 $this->bind
= array();
1408 for ($i=0; $i < $this->_numOfFields
; $i++
) {
1409 $o = $this->FetchField($i);
1410 $this->bind
[strtoupper($o->name
)] = $i;
1414 return $this->fields
[$this->bind
[strtoupper($colname)]];
1419 function _seek($row)
1426 return @OCIfetchinto
($this->_queryID
,$this->fields
,$this->fetchMode
);
1429 /* close() only needs to be called if you are worried about using too much memory while your script
1430 is running. All associated result memory for the specified result identifier will automatically be freed. */
1434 if ($this->connection
->_stmt
=== $this->_queryID
) $this->connection
->_stmt
= false;
1435 if (!empty($this->_refcursor
)) {
1436 OCIFreeCursor($this->_refcursor
);
1437 $this->_refcursor
= false;
1439 @OCIFreeStatement
($this->_queryID
);
1440 $this->_queryID
= false;
1444 function MetaType($t,$len=-1)
1446 if (is_object($t)) {
1448 $t = $fieldobj->type
;
1449 $len = $fieldobj->max_length
;
1451 switch (strtoupper($t)) {
1460 if (isset($this) && $len <= $this->blobSize
) return 'C';
1464 case 'LONG VARCHAR':
1469 case 'LONG VARBINARY':
1474 return ($this->connection
->datetime
) ?
'T' : 'D';
1477 case 'TIMESTAMP': return 'T';
1484 default: return 'N';
1489 class ADORecordSet_ext_oci8
extends ADORecordSet_oci8
{
1490 function ADORecordSet_ext_oci8($queryID,$mode=false)
1492 if ($mode === false) {
1493 global $ADODB_FETCH_MODE;
1494 $mode = $ADODB_FETCH_MODE;
1498 case ADODB_FETCH_ASSOC
:$this->fetchMode
= OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS
; break;
1499 case ADODB_FETCH_DEFAULT
:
1500 case ADODB_FETCH_BOTH
:$this->fetchMode
= OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS
; break;
1501 case ADODB_FETCH_NUM
:
1502 default: $this->fetchMode
= OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS
; break;
1504 $this->adodbFetchMode
= $mode;
1505 $this->_queryID
= $queryID;
1510 return adodb_movenext($this);