4 version V4.22 15 Apr 2004 (c) 2000-2004 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://php.weblogs.com/
12 Code contributed by George Fourlanos <fou@infomap.gr>
14 13 Nov 2000 jlim - removed all ora_* references.
19 Allows you to use a date format other than the Oracle Lite default. When a literal
20 character string appears where a date value is expected, the Oracle Lite database
21 tests the string to see if it matches the formats of Oracle, SQL-92, or the value
22 specified for this parameter in the POLITE.INI file. Setting this parameter also
23 defines the default format used in the TO_CHAR or TO_DATE functions when no
24 other format string is supplied.
26 For Oracle the default is dd-mon-yy or dd-mon-yyyy, and for SQL-92 the default is
27 yy-mm-dd or yyyy-mm-dd.
29 Using 'RR' in the format forces two-digit years less than or equal to 49 to be
30 interpreted as years in the 21st century (2000
\962049), and years over 50 as years in
31 the 20th century (1950
\961999). Setting the RR format as the default for all two-digit
32 year entries allows you to become year-2000 compliant. For example:
33 NLS_DATE_FORMAT='RR-MM-DD'
35 You can also modify the date format using the ALTER SESSION command.
38 class ADODB_oci8 extends ADOConnection {
39 var $databaseType = 'oci8';
40 var $dataProvider = 'oci8';
41 var $replaceQuote = "''"; // string to use to replace quotes
42 var $concat_operator='||';
43 var $sysDate = "TRUNC(SYSDATE)";
44 var $sysTimeStamp = 'SYSDATE';
45 var $metaDatabasesSQL = "SELECT USERNAME FROM ALL_USERS WHERE USERNAME NOT IN ('SYS','SYSTEM','DBSNMP','OUTLN') ORDER BY 1";
47 var $_commit = OCI_COMMIT_ON_SUCCESS;
48 var $_initdate = true; // init date to YYYY-MM-DD
49 var $metaTablesSQL = "select table_name,table_type from cat where table_type in ('TABLE','VIEW')";
50 var $metaColumnsSQL = "select cname,coltype,width, SCALE, PRECISION, NULLS, DEFAULTVAL from col where tname='%s' order by colno"; //changed by smondino@users.sourceforge. net
51 var $_bindInputArray = true;
53 var $_genIDSQL = "SELECT (%s.nextval) FROM DUAL";
54 var $_genSeqSQL = "CREATE SEQUENCE %s START WITH %s";
55 var $_dropSeqSQL = "DROP SEQUENCE %s";
56 var $hasAffectedRows = true;
57 var $random = "abs(mod(DBMS_RANDOM.RANDOM,10000001)/10000000)";
58 var $noNullStrings = false;
59 var $connectSID = false;
61 var $_hasOCIFetchStatement = false;
62 var $_getarray = false; // currently not working
63 var $leftOuter = ''; // oracle wierdness, $col = $value (+) for LEFT OUTER, $col (+)= $value for RIGHT OUTER
64 var $session_sharing_force_blob = false; // alter session on updateblob if set to true
65 var $firstrows = true; // enable first rows optimization on SelectLimit()
66 var $selectOffsetAlg1 = 100; // when to use 1st algorithm of selectlimit.
67 var $NLS_DATE_FORMAT = 'YYYY-MM-DD'; // To include time, use 'RRRR-MM-DD HH24:MI:SS'
68 var $useDBDateFormatForTextInput=false;
69 var $datetime = false; // MetaType('DATE') returns 'D' (datetime==false) or 'T' (datetime == true)
71 // var $ansiOuter = true; // if oracle9
75 $this->_hasOCIFetchStatement = ADODB_PHPVER >= 0x4200;
78 /* Function &MetaColumns($table) added by smondino@users.sourceforge.net*/
79 function &MetaColumns($table)
81 global $ADODB_FETCH_MODE;
83 $save = $ADODB_FETCH_MODE;
84 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
85 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
87 $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table)));
89 if (isset($savem)) $this->SetFetchMode($savem);
90 $ADODB_FETCH_MODE = $save;
91 if (!$rs) return false;
93 while (!$rs->EOF) { //print_r($rs->fields);
94 $fld = new ADOFieldObject();
95 $fld->name = $rs->fields[0];
96 $fld->type = $rs->fields[1];
97 $fld->max_length = $rs->fields[2];
98 $fld->scale = $rs->fields[3];
99 if ($rs->fields[1] == 'NUMBER' && $rs->fields[3] == 0) {
101 $fld->max_length = $rs->fields[4];
103 $fld->not_null = (strncmp($rs->fields[5], 'NOT',3) === 0);
104 $fld->binary = (strpos($fld->type,'BLOB') !== false);
105 $fld->default_value = $rs->fields[6];
107 if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld;
108 else $retarr[strtoupper($fld->name)] = $fld;
117 $rs =& $this->Execute("select TO_CHAR($this->sysTimeStamp,'YYYY-MM-DD HH24:MI:SS') from dual");
118 if ($rs && !$rs->EOF) return $this->UnixTimeStamp(reset($rs->fields));
125 Multiple modes of connection are supported:
128 $conn->Connect(false,'scott','tiger');
131 $conn->Connect(false,'scott','tiger',$tnsname);
132 $conn->Connect($tnsname,'scott','tiger');
134 c. Server + service name
135 $conn->Connect($serveraddress,'scott,'tiger',$service_name);
138 $conn->connectSID = true;
139 $conn->Connect($serveraddress,'scott,'tiger',$SID);
147 (ADDRESS = (PROTOCOL = TCP)(HOST = kermit)(PORT = 1523))
150 (SERVICE_NAME = natsoft.domain)
154 There are 3 connection modes, 0 = non-persistent, 1 = persistent, 2 = force new connection
157 function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$mode=0)
159 if (!function_exists('OCIPLogon')) return false;
162 $this->_errorMsg = false;
163 $this->_errorCode = false;
165 if($argHostname) { // added by Jorma Tuomainen <jorma.tuomainen@ppoy.fi>
166 if (empty($argDatabasename)) $argDatabasename = $argHostname;
168 if(strpos($argHostname,":")) {
169 $argHostinfo=explode(":",$argHostname);
170 $argHostname=$argHostinfo[0];
171 $argHostport=$argHostinfo[1];
176 if ($this->connectSID) {
177 $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
178 .")(PORT=$argHostport))(CONNECT_DATA=(SID=$argDatabasename)))";
180 $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
181 .")(PORT=$argHostport))(CONNECT_DATA=(SERVICE_NAME=$argDatabasename)))";
185 //if ($argHostname) print "<p>Connect: 1st argument should be left blank for $this->databaseType</p>";
187 $this->_connectionID = OCIPLogon($argUsername,$argPassword, $argDatabasename);
188 if ($this->_connectionID && $this->autoRollback) OCIrollback($this->_connectionID);
189 } else if ($mode==2) {
190 $this->_connectionID = OCINLogon($argUsername,$argPassword, $argDatabasename);
192 $this->_connectionID = OCILogon($argUsername,$argPassword, $argDatabasename);
194 if ($this->_connectionID === false) return false;
195 if ($this->_initdate) {
196 $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='".$this->NLS_DATE_FORMAT."'");
200 // Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production With the Partitioning option JServer Release 8.1.7.0.0 - Production
201 // $vers = OCIServerVersion($this->_connectionID);
202 // if (strpos($vers,'8i') !== false) $this->ansiOuter = true;
206 function ServerInfo()
208 $arr['compat'] = $this->GetOne('select value from sys.database_compatible_level');
209 $arr['description'] = @OCIServerVersion($this->_connectionID);
210 $arr['version'] = ADOConnection::_findvers($arr['description']);
213 // returns true or false
214 function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
216 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,1);
221 // returns true or false
222 function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
224 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,2);
227 function _affectedrows()
229 if (is_resource($this->_stmt)) return @OCIRowCount($this->_stmt);
233 function IfNull( $field, $ifNull )
235 return " NVL($field, $ifNull) "; // if Oracle
238 // format and return date string in database date format
241 if (empty($d) && $d !== 0) return 'null';
243 if (is_string($d)) $d = ADORecordSet::UnixDate($d);
244 return "TO_DATE(".adodb_date($this->fmtDate,$d).",'".$this->NLS_DATE_FORMAT."')";
248 // format and return date string in database timestamp format
249 function DBTimeStamp($ts)
251 if (empty($ts) && $ts !== 0) return 'null';
252 if (is_string($ts)) $ts = ADORecordSet::UnixTimeStamp($ts);
253 return 'TO_DATE('.adodb_date($this->fmtTimeStamp,$ts).",'RRRR-MM-DD, HH:MI:SS AM')";
256 function RowLock($tables,$where)
258 if ($this->autoCommit) $this->BeginTrans();
259 return $this->GetOne("select 1 as ignore from $tables where $where for update");
262 function &MetaTables($ttype=false,$showSchema=false,$mask=false)
265 $save = $this->metaTablesSQL;
266 $mask = $this->qstr(strtoupper($mask));
267 $this->metaTablesSQL .= " AND table_name like $mask";
269 $ret =& ADOConnection::MetaTables($ttype,$showSchema);
272 $this->metaTablesSQL = $save;
277 function BeginTrans()
279 if ($this->transOff) return true;
280 $this->transCnt += 1;
281 $this->autoCommit = false;
282 $this->_commit = OCI_DEFAULT;
286 function CommitTrans($ok=true)
288 if ($this->transOff) return true;
289 if (!$ok) return $this->RollbackTrans();
291 if ($this->transCnt) $this->transCnt -= 1;
292 $ret = OCIcommit($this->_connectionID);
293 $this->_commit = OCI_COMMIT_ON_SUCCESS;
294 $this->autoCommit = true;
298 function RollbackTrans()
300 if ($this->transOff) return true;
301 if ($this->transCnt) $this->transCnt -= 1;
302 $ret = OCIrollback($this->_connectionID);
303 $this->_commit = OCI_COMMIT_ON_SUCCESS;
304 $this->autoCommit = true;
309 function SelectDB($dbName)
316 if ($this->_errorMsg !== false) return $this->_errorMsg;
318 if (is_resource($this->_stmt)) $arr = @OCIerror($this->_stmt);
320 $arr = @OCIerror($this->_connectionID);
321 if ($arr === false) $arr = @OCIError();
322 if ($arr === false) return '';
324 $this->_errorMsg = $arr['message'];
325 $this->_errorCode = $arr['code'];
326 return $this->_errorMsg;
331 if ($this->_errorCode !== false) return $this->_errorCode;
333 if (is_resource($this->_stmt)) $arr = @OCIError($this->_stmt);
335 $arr = @OCIError($this->_connectionID);
336 if ($arr == false) $arr = @OCIError();
337 if ($arr == false) return '';
340 $this->_errorMsg = $arr['message'];
341 $this->_errorCode = $arr['code'];
346 // Format date column in sql string given an input format that understands Y M D
347 function SQLDate($fmt, $col=false)
349 if (!$col) $col = $this->sysTimeStamp;
350 $s = 'TO_CHAR('.$col.",'";
353 for ($i=0; $i < $len; $i++) {
399 // handle escape characters...
402 $ch = substr($fmt,$i,1);
404 if (strpos('-/.:;, ',$ch) !== false) $s .= $ch;
405 else $s .= '"'.$ch.'"';
414 This algorithm makes use of
417 The FIRST_ROWS hint explicitly chooses the approach to optimize response time,
418 that is, minimum resource usage to return the first row. Results will be returned
419 as soon as they are identified.
421 b. Uses rownum tricks to obtain only the required rows from a given offset.
422 As this uses complicated sql statements, we only use this if the $offset >= 100.
423 This idea by Tomas V V Cox.
425 This implementation does not appear to work with oracle 8.0.5 or earlier. Comment
426 out this function then, and the slower SelectLimit() in the base class will be used.
428 function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
430 // seems that oracle only supports 1 hint comment in 8i
431 if ($this->firstrows) {
432 if (strpos($sql,'/*+') !== false)
433 $sql = str_replace('/*+ ','/*+FIRST_ROWS ',$sql);
435 $sql = preg_replace('/^[ \t\n]*select/i','SELECT /*+FIRST_ROWS*/',$sql);
438 if ($offset < $this->selectOffsetAlg1) {
440 if ($offset > 0) $nrows += $offset;
441 //$inputarr['adodb_rownum'] = $nrows;
442 if ($this->databaseType == 'oci8po') {
443 $sql = "select * from ($sql) where rownum <= ?";
445 $sql = "select * from ($sql) where rownum <= :adodb_offset";
447 $inputarr['adodb_offset'] = $nrows;
450 // note that $nrows = 0 still has to work ==> no rows returned
452 $rs =& ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
456 // Algorithm by Tomas V V Cox, from PEAR DB oci8.php
458 // Let Oracle return the name of the columns
459 $q_fields = "SELECT * FROM ($sql) WHERE NULL = NULL";
460 if (!$stmt = OCIParse($this->_connectionID, $q_fields)) {
464 if (is_array($inputarr)) {
465 foreach($inputarr as $k => $v) {
467 if (sizeof($v) == 2) // suggested by g.giunta@libero.
468 OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]);
470 OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
473 if ($v === ' ') $len = 1;
474 if (isset($bindarr)) { // is prepared sql, so no need to ocibindbyname again
476 } else { // dynamic sql, so rebind every time
477 OCIBindByName($stmt,":$k",$inputarr[$k],$len);
483 if (!OCIExecute($stmt, OCI_DEFAULT)) {
484 OCIFreeStatement($stmt);
488 $ncols = OCINumCols($stmt);
489 for ( $i = 1; $i <= $ncols; $i++ ) {
490 $cols[] = '"'.OCIColumnName($stmt, $i).'"';
494 OCIFreeStatement($stmt);
495 $fields = implode(',', $cols);
497 $offset += 1; // in Oracle rownum starts at 1
499 if ($this->databaseType == 'oci8po') {
500 $sql = "SELECT $fields FROM".
501 "(SELECT rownum as adodb_rownum, $fields FROM".
502 " ($sql) WHERE rownum <= ?".
503 ") WHERE adodb_rownum >= ?";
505 $sql = "SELECT $fields FROM".
506 "(SELECT rownum as adodb_rownum, $fields FROM".
507 " ($sql) WHERE rownum <= :adodb_nrows".
508 ") WHERE adodb_rownum >= :adodb_offset";
510 $inputarr['adodb_nrows'] = $nrows;
511 $inputarr['adodb_offset'] = $offset;
513 if ($secs2cache>0) $rs =& $this->CacheExecute($secs2cache, $sql,$inputarr);
514 else $rs =& $this->Execute($sql,$inputarr);
522 * Store BLOBs and CLOBs
524 * Example: to store $var in a blob
526 * $conn->Execute('insert into TABLE (id,ablob) values(12,empty_blob())');
527 * $conn->UpdateBlob('TABLE', 'ablob', $varHoldingBlob, 'ID=12', 'BLOB');
529 * $blobtype supports 'BLOB' and 'CLOB', but you need to change to 'empty_clob()'.
531 * to get length of LOB:
532 * select DBMS_LOB.GETLENGTH(ablob) from TABLE
534 * If you are using CURSOR_SHARING = force, it appears this will case a segfault
535 * under oracle 8.1.7.0. Run:
536 * $db->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT');
537 * before UpdateBlob() then...
540 function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
543 //if (strlen($val) < 4000) return $this->Execute("UPDATE $table SET $column=:blob WHERE $where",array('blob'=>$val)) != false;
545 switch(strtoupper($blobtype)) {
546 default: ADOConnection::outp("<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
547 case 'BLOB': $type = OCI_B_BLOB; break;
548 case 'CLOB': $type = OCI_B_CLOB; break;
551 if ($this->databaseType == 'oci8po')
552 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
554 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
556 $desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB);
557 $arr['blob'] = array($desc,-1,$type);
558 if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT');
559 $commit = $this->autoCommit;
560 if ($commit) $this->BeginTrans();
561 $rs = ADODB_oci8::Execute($sql,$arr);
562 if ($rez = !empty($rs)) $desc->save($val);
564 if ($commit) $this->CommitTrans();
565 if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=FORCE');
567 if ($rez) $rs->Close();
572 * Usage: store file pointed to by $var in a blob
574 function UpdateBlobFile($table,$column,$val,$where,$blobtype='BLOB')
576 switch(strtoupper($blobtype)) {
577 default: ADOConnection::outp( "<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
578 case 'BLOB': $type = OCI_B_BLOB; break;
579 case 'CLOB': $type = OCI_B_CLOB; break;
582 if ($this->databaseType == 'oci8po')
583 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
585 $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
587 $desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB);
588 $arr['blob'] = array($desc,-1,$type);
591 $rs = ADODB_oci8::Execute($sql,$arr);
592 if ($rez = !empty($rs)) $desc->savefile($val);
594 $this->CommitTrans();
596 if ($rez) $rs->Close();
604 $stmt = $this->Prepare('insert into emp (empno, ename) values (:empno, :ename)');
606 function Prepare($sql,$cursor=false)
610 $stmt = OCIParse($this->_connectionID,$sql);
612 if (!$stmt) return false;
616 if (@OCIStatementType($stmt) == 'BEGIN') {
617 return array($sql,$stmt,0,$BINDNUM, ($cursor) ? OCINewCursor($this->_connectionID) : false);
620 return array($sql,$stmt,0,$BINDNUM);
624 Call an oracle stored procedure and return a cursor variable.
625 Convert the cursor variable into a recordset.
626 Concept by Robert Tuttle robert@ud.com
629 Note: we return a cursor variable in :RS2
630 $rs = $db->ExecuteCursor("BEGIN adodb.open_tab(:RS2); END;",'RS2');
632 $rs = $db->ExecuteCursor(
633 "BEGIN :RS2 = adodb.getdata(:VAR1); END;",
635 array('VAR1' => 'Mr Bean'));
638 function &ExecuteCursor($sql,$cursorName='rs',$params=false)
640 $stmt = ADODB_oci8::Prepare($sql,true); # true to allocate OCINewCursor
642 if (is_array($stmt) && sizeof($stmt) >= 5) {
643 $this->Parameter($stmt, $ignoreCur, $cursorName, false, -1, OCI_B_CURSOR);
645 foreach($params as $k => $v) {
646 $this->Parameter($stmt,$params[$k], $k);
650 return $this->Execute($stmt);
654 Bind a variable -- very, very fast for executing repeated statements in oracle.
656 for ($i = 0; $i < $max; $i++) {
657 $p1 = ?; $p2 = ?; $p3 = ?;
658 $this->Execute("insert into table (col0, col1, col2) values (:0, :1, :2)",
663 $stmt = $DB->Prepare("insert into table (col0, col1, col2) values (:0, :1, :2)");
664 $DB->Bind($stmt, $p1);
665 $DB->Bind($stmt, $p2);
666 $DB->Bind($stmt, $p3);
667 for ($i = 0; $i < $max; $i++) {
668 $p1 = ?; $p2 = ?; $p3 = ?;
673 ** Test table has 3 cols, and 1 index. Test to insert 1000 records
674 Time 0.6081s (1644.60 inserts/sec) with direct OCIParse/OCIExecute
675 Time 0.6341s (1577.16 inserts/sec) with ADOdb Prepare/Bind/Execute
676 Time 1.5533s ( 643.77 inserts/sec) with pure SQL using Execute
678 Now if PHP only had batch/bulk updating like Java or PL/SQL...
680 Note that the order of parameters differs from OCIBindByName,
681 because we default the names to :0, :1, :2
683 function Bind(&$stmt,&$var,$size=4000,$type=false,$name=false)
685 if (!is_array($stmt)) return false;
687 if (($type == OCI_B_CURSOR) && sizeof($stmt) >= 5) {
688 return OCIBindByName($stmt[1],":".$name,$stmt[4],$size,$type);
691 if ($name == false) {
692 if ($type !== false) $rez = OCIBindByName($stmt[1],":".$name,$var,$size,$type);
693 else $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size); // +1 byte for null terminator
695 } else if ($type == OCI_B_BLOB){
696 //we have to create a new Descriptor here
697 $_blob = OCINewDescriptor($this->_connectionID, OCI_D_LOB);
698 $rez = OCIBindByName($stmt[1], ":".$name, &$_blob, -1, OCI_B_BLOB);
701 if ($type !== false) $rez = OCIBindByName($stmt[1],":".$name,$var,$size,$type);
702 else $rez = OCIBindByName($stmt[1],":".$name,$var,$size); // +1 byte for null terminator
708 function Param($name)
715 $stmt = $db->Prepare('select * from table where id =:myid and group=:group');
716 $db->Parameter($stmt,$id,'myid');
717 $db->Parameter($stmt,$group,'group');
720 @param $stmt Statement returned by Prepare() or PrepareSP().
721 @param $var PHP variable to bind to
722 @param $name Name of stored procedure variable name to bind to.
723 @param [$isOutput] Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8.
724 @param [$maxLen] Holds an maximum length of the variable.
725 @param [$type] The data type of $var. Legal values depend on driver.
727 See OCIBindByName documentation at php.net.
729 function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
732 $prefix = ($isOutput) ? 'Out' : 'In';
733 $ztype = (empty($type)) ? 'false' : $type;
734 ADOConnection::outp( "{$prefix}Parameter(\$stmt, \$php_var='$var', \$name='$name', \$maxLen=$maxLen, \$type=$ztype);");
736 return $this->Bind($stmt,$var,$maxLen,$type,$name);
740 returns query ID if successful, otherwise false
741 this version supports:
743 1. $db->execute('select * from table');
745 2. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
746 $db->execute($prepared_statement, array(1,2,3));
748 3. $db->execute('insert into table (a,b,c) values (:a,:b,:c)',array('a'=>1,'b'=>2,'c'=>3));
750 4. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
751 $db->$bind($stmt,1); $db->bind($stmt,2); $db->bind($stmt,3);
754 function _query($sql,$inputarr)
757 if (is_array($sql)) { // is prepared sql
760 // we try to bind to permanent array, so that OCIBindByName is persistent
761 // and carried out once only - note that max array element size is 4000 chars
762 if (is_array($inputarr)) {
764 if (isset($this->_bind[$bindpos])) {
765 // all tied up already
766 $bindarr = &$this->_bind[$bindpos];
768 // one statement to bind them all
770 foreach($inputarr as $k => $v) {
772 OCIBindByName($stmt,":$k",$bindarr[$k],4000);
774 $this->_bind[$bindpos] = &$bindarr;
778 $stmt=OCIParse($this->_connectionID,$sql);
781 $this->_stmt = $stmt;
782 if (!$stmt) return false;
784 if (defined('ADODB_PREFETCH_ROWS')) @OCISetPrefetch($stmt,ADODB_PREFETCH_ROWS);
786 if (is_array($inputarr)) {
787 foreach($inputarr as $k => $v) {
789 if (sizeof($v) == 2) // suggested by g.giunta@libero.
790 OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]);
792 OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
794 if ($this->debug==99) echo "name=:$k",' var='.$inputarr[$k][0],' len='.$v[1],' type='.$v[2],'<br>';
797 if ($v === ' ') $len = 1;
798 if (isset($bindarr)) { // is prepared sql, so no need to ocibindbyname again
800 } else { // dynamic sql, so rebind every time
801 OCIBindByName($stmt,":$k",$inputarr[$k],$len);
807 $this->_errorMsg = false;
808 $this->_errorCode = false;
809 if (OCIExecute($stmt,$this->_commit)) {
811 switch (@OCIStatementType($stmt)) {
816 if (is_array($sql) && !empty($sql[4])) {
818 if (is_resource($cursor)) {
819 $ok = OCIExecute($cursor);
824 if (is_resource($stmt)) {
825 OCIFreeStatement($stmt);
832 // ociclose -- no because it could be used in a LOB?
839 // returns true or false
842 if (!$this->autoCommit) OCIRollback($this->_connectionID);
843 OCILogoff($this->_connectionID);
844 $this->_stmt = false;
845 $this->_connectionID = false;
848 function MetaPrimaryKeys($table, $owner=false,$internalKey=false)
850 if ($internalKey) return array('ROWID');
852 // tested with oracle 8.1.7
853 $table = strtoupper($table);
855 $owner_clause = "AND ((a.OWNER = b.OWNER) AND (a.OWNER = UPPER('$owner')))";
862 SELECT /*+ RULE */ distinct b.column_name
863 FROM {$ptab}CONSTRAINTS a
864 , {$ptab}CONS_COLUMNS b
865 WHERE ( UPPER(b.table_name) = ('$table'))
866 AND (UPPER(a.table_name) = ('$table') and a.constraint_type = 'P')
868 AND (a.constraint_name = b.constraint_name)";
870 $rs = $this->Execute($sql);
871 if ($rs && !$rs->EOF) {
872 $arr =& $rs->GetArray();
874 foreach($arr as $v) {
882 // http://gis.mit.edu/classes/11.521/sqlnotes/referential_integrity.html
883 function MetaForeignKeys($table, $owner=false)
885 global $ADODB_FETCH_MODE;
887 $save = $ADODB_FETCH_MODE;
888 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
889 $table = $this->qstr(strtoupper($table));
891 $owner = $this->user;
896 $owner = ' and owner='.$this->qstr(strtoupper($owner));
899 "select constraint_name,r_owner,r_constraint_name
900 from {$tabp}constraints
901 where constraint_type = 'R' and table_name = $table $owner";
903 $constraints =& $this->GetArray($sql);
905 foreach($constraints as $constr) {
906 $cons = $this->qstr($constr[0]);
907 $rowner = $this->qstr($constr[1]);
908 $rcons = $this->qstr($constr[2]);
909 $cols = $this->GetArray("select column_name from {$tabp}cons_columns where constraint_name=$cons $owner order by position");
910 $tabcol = $this->GetArray("select table_name,column_name from {$tabp}cons_columns where owner=$rowner and constraint_name=$rcons order by position");
912 if ($cols && $tabcol)
913 for ($i=0, $max=sizeof($cols); $i < $max; $i++) {
914 $arr[$tabcol[$i][0]] = $cols[$i][0].'='.$tabcol[$i][1];
917 $ADODB_FETCH_MODE = $save;
935 * An example is $db->qstr("Don't bother",magic_quotes_runtime());
937 * @param s the string to quote
938 * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc().
939 * This undoes the stupidity of magic quotes for GPC.
941 * @return quoted string to be sent back to database
943 function qstr($s,$magic_quotes=false)
947 if (is_array($s)) adodb_backtrace();
948 if ($this->noNullStrings && strlen($s)==0)$s = ' ';
949 if (!$magic_quotes) {
950 if ($this->replaceQuote[0] == '\\'){
951 $s = str_replace('\\','\\\\',$s);
953 return "'".str_replace("'",$this->replaceQuote,$s)."'";
956 // undo magic quotes for "
957 $s = str_replace('\\"','"',$s);
959 if ($this->replaceQuote == "\\'") // ' already quoted, no need to change anything
961 else {// change \' to '' for sybase/mssql
962 $s = str_replace('\\\\','\\',$s);
963 return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
969 /*--------------------------------------------------------------------------------------
970 Class Name: Recordset
971 --------------------------------------------------------------------------------------*/
973 class ADORecordset_oci8 extends ADORecordSet {
975 var $databaseType = 'oci8';
980 function ADORecordset_oci8($queryID,$mode=false)
982 if ($mode === false) {
983 global $ADODB_FETCH_MODE;
984 $mode = $ADODB_FETCH_MODE;
989 case ADODB_FETCH_NUM: $this->fetchMode = OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
990 case ADODB_FETCH_ASSOC:$this->fetchMode = OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
991 case ADODB_FETCH_DEFAULT:
992 case ADODB_FETCH_BOTH:$this->fetchMode = OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
995 $this->_queryID = $queryID;
1001 if ($this->_inited) return;
1003 $this->_inited = true;
1004 if ($this->_queryID) {
1006 $this->_currentRow = 0;
1008 $this->EOF = !$this->_fetch();
1011 // based on idea by Gaetano Giunta to detect unusual oracle errors
1012 // see http://phplens.com/lens/lensforum/msgs.php?id=6771
1013 $err = OCIError($this->_queryID);
1014 if ($err && $this->connection->debug) ADOConnection::outp($err);
1017 if (!is_array($this->fields)) {
1018 $this->_numOfRows = 0;
1019 $this->fields = array();
1022 $this->fields = array();
1023 $this->_numOfRows = 0;
1024 $this->_numOfFields = 0;
1031 $this->_numOfRows = -1;
1032 $this->_numOfFields = OCInumcols($this->_queryID);
1033 if ($this->_numOfFields>0) {
1034 $this->_fieldobjs = array();
1035 $max = $this->_numOfFields;
1036 for ($i=0;$i<$max; $i++) $this->_fieldobjs[] = $this->_FetchField($i);
1040 /* Returns: an object containing field information.
1041 Get column information in the Recordset object. fetchField() can be used in order to obtain information about
1042 fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
1043 fetchField() is retrieved. */
1045 function &_FetchField($fieldOffset = -1)
1047 $fld = new ADOFieldObject;
1049 $fld->name =OCIcolumnname($this->_queryID, $fieldOffset);
1050 $fld->type = OCIcolumntype($this->_queryID, $fieldOffset);
1051 $fld->max_length = OCIcolumnsize($this->_queryID, $fieldOffset);
1052 if ($fld->type == 'NUMBER') {
1053 $p = OCIColumnPrecision($this->_queryID, $fieldOffset);
1054 $sc = OCIColumnScale($this->_queryID, $fieldOffset);
1055 if ($p != 0 && $sc == 0) $fld->type = 'INT';
1056 //echo " $this->name ($p.$sc) ";
1061 /* For some reason, OCIcolumnname fails when called after _initrs() so we cache it */
1062 function &FetchField($fieldOffset = -1)
1064 return $this->_fieldobjs[$fieldOffset];
1068 // 10% speedup to move MoveNext to child class
1071 //global $ADODB_EXTENSION;if ($ADODB_EXTENSION) return @adodb_movenext($this);
1073 if ($this->EOF) return false;
1075 $this->_currentRow++;
1076 if(@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode))
1083 /* Optimize SelectLimit() by using OCIFetch() instead of OCIFetchInto() */
1084 function &GetArrayLimit($nrows,$offset=-1)
1087 $arr =& $this->GetArray($nrows);
1090 for ($i=1; $i < $offset; $i++)
1091 if (!@OCIFetch($this->_queryID)) return array();
1093 if (!@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) return array();
1096 while (!$this->EOF && $nrows != $cnt) {
1097 $results[$cnt++] = $this->fields;
1105 /* Use associative array to get fields array */
1106 function Fields($colname)
1109 $this->bind = array();
1110 for ($i=0; $i < $this->_numOfFields; $i++) {
1111 $o = $this->FetchField($i);
1112 $this->bind[strtoupper($o->name)] = $i;
1116 return $this->fields[$this->bind[strtoupper($colname)]];
1121 function _seek($row)
1128 return @OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode);
1131 /* close() only needs to be called if you are worried about using too much memory while your script
1132 is running. All associated result memory for the specified result identifier will automatically be freed. */
1136 if ($this->connection->_stmt === $this->_queryID) $this->connection->_stmt = false;
1137 OCIFreeStatement($this->_queryID);
1138 $this->_queryID = false;
1142 function MetaType($t,$len=-1)
1144 if (is_object($t)) {
1146 $t = $fieldobj->type;
1147 $len = $fieldobj->max_length;
1149 switch (strtoupper($t)) {
1158 if (isset($this) && $len <= $this->blobSize) return 'C';
1162 case 'LONG VARCHAR':
1167 case 'LONG VARBINARY':
1172 return ($this->connection->datetime) ? 'T' : 'D';
1175 case 'TIMESTAMP': return 'T';
1182 default: return 'N';