/src/typo3_src-4.2.0alpha1/typo3/sysext/adodb/adodb/drivers/adodb-oci8.inc.php

00001 <?php
00002 /*
00003 
00004   version V4.94 23 Jan 2007 (c) 2000-2007 John Lim. All rights reserved.
00005 
00006   Released under both BSD license and Lesser GPL library license. 
00007   Whenever there is any discrepancy between the two licenses, 
00008   the BSD license will take precedence.
00009 
00010   Latest version is available at http://adodb.sourceforge.net
00011   
00012   Code contributed by George Fourlanos <fou@infomap.gr>
00013   
00014   13 Nov 2000 jlim - removed all ora_* references.
00015 */
00016 
00017 // security - hide paths
00018 if (!defined('ADODB_DIR')) die();
00019 
00020 /*
00021 NLS_Date_Format
00022 Allows you to use a date format other than the Oracle Lite default. When a literal
00023 character string appears where a date value is expected, the Oracle Lite database
00024 tests the string to see if it matches the formats of Oracle, SQL-92, or the value
00025 specified for this parameter in the POLITE.INI file. Setting this parameter also
00026 defines the default format used in the TO_CHAR or TO_DATE functions when no
00027 other format string is supplied.
00028 
00029 For Oracle the default is dd-mon-yy or dd-mon-yyyy, and for SQL-92 the default is
00030 yy-mm-dd or yyyy-mm-dd.
00031 
00032 Using 'RR' in the format forces two-digit years less than or equal to 49 to be
00033 interpreted as years in the 21st century (2000–2049), and years over 50 as years in
00034 the 20th century (1950–1999). Setting the RR format as the default for all two-digit
00035 year entries allows you to become year-2000 compliant. For example:
00036 NLS_DATE_FORMAT='RR-MM-DD'
00037 
00038 You can also modify the date format using the ALTER SESSION command. 
00039 */
00040 
00041 # define the LOB descriptor type for the given type
00042 # returns false if no LOB descriptor
00043 function oci_lob_desc($type) {
00044         switch ($type) {
00045                 case OCI_B_BFILE: $result = OCI_D_FILE; break;
00046                 case OCI_B_CFILEE: $result = OCI_D_FILE; break;
00047                 case OCI_B_CLOB: $result = OCI_D_LOB; break;
00048                 case OCI_B_BLOB: $result = OCI_D_LOB; break;
00049                 case OCI_B_ROWID: $result = OCI_D_ROWID; break;
00050                 default: $result = false; break;
00051         }
00052         return $result;
00053 }
00054 
00055 class ADODB_oci8 extends ADOConnection {
00056         var $databaseType = 'oci8';
00057         var $dataProvider = 'oci8';
00058         var $replaceQuote = "''"; // string to use to replace quotes
00059         var $concat_operator='||';
00060         var $sysDate = "TRUNC(SYSDATE)";
00061         var $sysTimeStamp = 'SYSDATE';
00062         var $metaDatabasesSQL = "SELECT USERNAME FROM ALL_USERS WHERE USERNAME NOT IN ('SYS','SYSTEM','DBSNMP','OUTLN') ORDER BY 1";
00063         var $_stmt;
00064         var $_commit = OCI_COMMIT_ON_SUCCESS;
00065         var $_initdate = true; // init date to YYYY-MM-DD
00066         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
00067         var $metaColumnsSQL = "select cname,coltype,width, SCALE, PRECISION, NULLS, DEFAULTVAL from col where tname='%s' order by colno"; //changed by smondino@users.sourceforge. net
00068         var $_bindInputArray = true;
00069         var $hasGenID = true;
00070         var $_genIDSQL = "SELECT (%s.nextval) FROM DUAL";
00071         var $_genSeqSQL = "CREATE SEQUENCE %s START WITH %s";
00072         var $_dropSeqSQL = "DROP SEQUENCE %s";
00073         var $hasAffectedRows = true;
00074         var $random = "abs(mod(DBMS_RANDOM.RANDOM,10000001)/10000000)";
00075         var $noNullStrings = false;
00076         var $connectSID = false;
00077         var $_bind = false;
00078         var $_nestedSQL = true;
00079         var $_hasOCIFetchStatement = false;
00080         var $_getarray = false; // currently not working
00081         var $leftOuter = '';  // oracle wierdness, $col = $value (+) for LEFT OUTER, $col (+)= $value for RIGHT OUTER
00082         var $session_sharing_force_blob = false; // alter session on updateblob if set to true 
00083         var $firstrows = true; // enable first rows optimization on SelectLimit()
00084         var $selectOffsetAlg1 = 100; // when to use 1st algorithm of selectlimit.
00085         var $NLS_DATE_FORMAT = 'YYYY-MM-DD';  // To include time, use 'RRRR-MM-DD HH24:MI:SS'
00086         var $useDBDateFormatForTextInput=false;
00087         var $datetime = false; // MetaType('DATE') returns 'D' (datetime==false) or 'T' (datetime == true)
00088         var $_refLOBs = array();
00089         
00090         // var $ansiOuter = true; // if oracle9
00091     
00092         function ADODB_oci8() 
00093         {
00094                 $this->_hasOCIFetchStatement = ADODB_PHPVER >= 0x4200;
00095                 if (defined('ADODB_EXTENSION')) $this->rsPrefix .= 'ext_';
00096         }
00097         
00098         /*  Function &MetaColumns($table) added by smondino@users.sourceforge.net*/
00099         function &MetaColumns($table) 
00100         {
00101         global $ADODB_FETCH_MODE;
00102         
00103                 $false = false;
00104                 $save = $ADODB_FETCH_MODE;
00105                 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
00106                 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
00107                 
00108                 $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table));
00109                 
00110                 if (isset($savem)) $this->SetFetchMode($savem);
00111                 $ADODB_FETCH_MODE = $save;
00112                 if (!$rs) {
00113                         return $false;
00114                 }
00115                 $retarr = array();
00116                 while (!$rs->EOF) { //print_r($rs->fields);
00117                         $fld = new ADOFieldObject();
00118                         $fld->name = $rs->fields[0];
00119                         $fld->type = $rs->fields[1];
00120                         $fld->max_length = $rs->fields[2];
00121                         $fld->scale = $rs->fields[3];
00122                         if ($rs->fields[1] == 'NUMBER') {
00123                                 if ($rs->fields[3] == 0) $fld->type = 'INT';
00124                         $fld->max_length = $rs->fields[4];
00125                 }       
00126                         $fld->not_null = (strncmp($rs->fields[5], 'NOT',3) === 0);
00127                         $fld->binary = (strpos($fld->type,'BLOB') !== false);
00128                         $fld->default_value = $rs->fields[6];
00129                         
00130                         if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld;     
00131                         else $retarr[strtoupper($fld->name)] = $fld;
00132                         $rs->MoveNext();
00133                 }
00134                 $rs->Close();
00135                 if (empty($retarr))
00136                         return  $false;
00137                 else 
00138                         return $retarr;
00139         }
00140         
00141         function Time()
00142         {
00143                 $rs =& $this->Execute("select TO_CHAR($this->sysTimeStamp,'YYYY-MM-DD HH24:MI:SS') from dual");
00144                 if ($rs && !$rs->EOF) return $this->UnixTimeStamp(reset($rs->fields));
00145                 
00146                 return false;
00147         }
00148  
00149 /*
00150 
00151   Multiple modes of connection are supported:
00152   
00153   a. Local Database
00154     $conn->Connect(false,'scott','tiger');
00155   
00156   b. From tnsnames.ora
00157     $conn->Connect(false,'scott','tiger',$tnsname); 
00158     $conn->Connect($tnsname,'scott','tiger'); 
00159   
00160   c. Server + service name
00161     $conn->Connect($serveraddress,'scott,'tiger',$service_name);
00162   
00163   d. Server + SID
00164         $conn->connectSID = true;
00165         $conn->Connect($serveraddress,'scott,'tiger',$SID);
00166 
00167 
00168 Example TNSName:
00169 ---------------
00170 NATSOFT.DOMAIN =
00171   (DESCRIPTION =
00172         (ADDRESS_LIST =
00173           (ADDRESS = (PROTOCOL = TCP)(HOST = kermit)(PORT = 1523))
00174         )
00175         (CONNECT_DATA =
00176           (SERVICE_NAME = natsoft.domain)
00177         )
00178   )
00179   
00180   There are 3 connection modes, 0 = non-persistent, 1 = persistent, 2 = force new connection
00181         
00182 */
00183         function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$mode=0)
00184         {
00185                 if (!function_exists('OCIPLogon')) return null;
00186                 
00187                 
00188         $this->_errorMsg = false;
00189                 $this->_errorCode = false;
00190                 
00191                 if($argHostname) { // added by Jorma Tuomainen <jorma.tuomainen@ppoy.fi>
00192                         if (empty($argDatabasename)) $argDatabasename = $argHostname;
00193                         else {
00194                                 if(strpos($argHostname,":")) {
00195                                         $argHostinfo=explode(":",$argHostname);
00196                                         $argHostname=$argHostinfo[0];
00197                                         $argHostport=$argHostinfo[1];
00198                                 } else {
00199                                         $argHostport = empty($this->port)?  "1521" : $this->port;
00200                                 }
00201                                 
00202                                 if ($this->connectSID) {
00203                                         $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
00204                                         .")(PORT=$argHostport))(CONNECT_DATA=(SID=$argDatabasename)))";
00205                                 } else
00206                                         $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
00207                                         .")(PORT=$argHostport))(CONNECT_DATA=(SERVICE_NAME=$argDatabasename)))";
00208                         }
00209                 }
00210                                 
00211                 //if ($argHostname) print "<p>Connect: 1st argument should be left blank for $this->databaseType</p>";
00212                 if ($mode==1) {
00213                         $this->_connectionID = ($this->charSet) ? 
00214                                 OCIPLogon($argUsername,$argPassword, $argDatabasename,$this->charSet)
00215                                 :
00216                                 OCIPLogon($argUsername,$argPassword, $argDatabasename)
00217                                 ;
00218                         if ($this->_connectionID && $this->autoRollback)  OCIrollback($this->_connectionID);
00219                 } else if ($mode==2) {
00220                         $this->_connectionID = ($this->charSet) ? 
00221                                 OCINLogon($argUsername,$argPassword, $argDatabasename,$this->charSet)
00222                                 :
00223                                 OCINLogon($argUsername,$argPassword, $argDatabasename);
00224                                 
00225                 } else {
00226                         $this->_connectionID = ($this->charSet) ? 
00227                                 OCILogon($argUsername,$argPassword, $argDatabasename,$this->charSet)
00228                                 :
00229                                 OCILogon($argUsername,$argPassword, $argDatabasename);
00230                 }
00231                 if (!$this->_connectionID) return false;
00232                 if ($this->_initdate) {
00233                         $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='".$this->NLS_DATE_FORMAT."'");
00234                 }
00235                 
00236                 // looks like: 
00237                 // Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production With the Partitioning option JServer Release 8.1.7.0.0 - Production
00238                 // $vers = OCIServerVersion($this->_connectionID);
00239                 // if (strpos($vers,'8i') !== false) $this->ansiOuter = true;
00240                 return true;
00241         }
00242         
00243         function ServerInfo()
00244         {
00245                 $arr['compat'] = $this->GetOne('select value from sys.database_compatible_level');
00246                 $arr['description'] = @OCIServerVersion($this->_connectionID);
00247                 $arr['version'] = ADOConnection::_findvers($arr['description']);
00248                 return $arr;
00249         }
00250                 // returns true or false
00251         function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
00252         {
00253                 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,1);
00254         }
00255         
00256         // returns true or false
00257         function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
00258         {
00259                 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,2);
00260         }
00261         
00262         function _affectedrows()
00263         {
00264                 if (is_resource($this->_stmt)) return @OCIRowCount($this->_stmt);
00265                 return 0;
00266         }
00267         
00268         function IfNull( $field, $ifNull ) 
00269         {
00270                 return " NVL($field, $ifNull) "; // if Oracle
00271         }
00272         
00273         // format and return date string in database date format
00274         function DBDate($d)
00275         {
00276                 if (empty($d) && $d !== 0) return 'null';
00277                 
00278                 if (is_string($d)) $d = ADORecordSet::UnixDate($d);
00279                 return "TO_DATE(".adodb_date($this->fmtDate,$d).",'".$this->NLS_DATE_FORMAT."')";
00280         }
00281 
00282         function BindDate($d)
00283         {
00284                 $d = ADOConnection::DBDate($d);
00285                 if (strncmp($d,"'",1)) return $d;
00286                 
00287                 return substr($d,1,strlen($d)-2);
00288         }
00289         
00290         function BindTimeStamp($d)
00291         {
00292                 $d = ADOConnection::DBTimeStamp($d);
00293                 if (strncmp($d,"'",1)) return $d;
00294                 
00295                 return substr($d,1,strlen($d)-2);
00296         }
00297         
00298         // format and return date string in database timestamp format
00299         function DBTimeStamp($ts)
00300         {
00301                 if (empty($ts) && $ts !== 0) return 'null';
00302                 if (is_string($ts)) $ts = ADORecordSet::UnixTimeStamp($ts);
00303                 return 'TO_DATE('.adodb_date($this->fmtTimeStamp,$ts).",'RRRR-MM-DD, HH:MI:SS AM')";
00304         }
00305         
00306         function RowLock($tables,$where,$flds='1 as ignore') 
00307         {
00308                 if ($this->autoCommit) $this->BeginTrans();
00309                 return $this->GetOne("select $flds from $tables where $where for update");
00310         }
00311         
00312         function &MetaTables($ttype=false,$showSchema=false,$mask=false) 
00313         {
00314                 if ($mask) {
00315                         $save = $this->metaTablesSQL;
00316                         $mask = $this->qstr(strtoupper($mask));
00317                         $this->metaTablesSQL .= " AND upper(table_name) like $mask";
00318                 }
00319                 $ret =& ADOConnection::MetaTables($ttype,$showSchema);
00320                 
00321                 if ($mask) {
00322                         $this->metaTablesSQL = $save;
00323                 }
00324                 return $ret;
00325         }
00326         
00327         // Mark Newnham 
00328         function &MetaIndexes ($table, $primary = FALSE, $owner=false)
00329         {
00330         // save old fetch mode
00331         global $ADODB_FETCH_MODE;
00332 
00333         $save = $ADODB_FETCH_MODE;
00334         $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
00335 
00336         if ($this->fetchMode !== FALSE) {
00337                $savem = $this->SetFetchMode(FALSE);
00338         }
00339 
00340                 // get index details
00341                 $table = strtoupper($table);
00342 
00343                 // get Primary index
00344                 $primary_key = '';
00345 
00346                 $false = false;
00347                 $rs = $this->Execute(sprintf("SELECT * FROM ALL_CONSTRAINTS WHERE UPPER(TABLE_NAME)='%s' AND CONSTRAINT_TYPE='P'",$table));
00348                 if ($row = $rs->FetchRow())
00349                    $primary_key = $row[1]; //constraint_name
00350 
00351                 if ($primary==TRUE && $primary_key=='') {
00352                          if (isset($savem)) 
00353                 $this->SetFetchMode($savem);
00354                         $ADODB_FETCH_MODE = $save;
00355                         return $false; //There is no primary key
00356                 }
00357 
00358         $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));
00359 
00360                 
00361         if (!is_object($rs)) {
00362                         if (isset($savem)) 
00363                                 $this->SetFetchMode($savem);
00364                         $ADODB_FETCH_MODE = $save;
00365             return $false;
00366         }
00367 
00368                 $indexes = array ();
00369         // parse index data into array
00370 
00371         while ($row = $rs->FetchRow()) {
00372                         if ($primary && $row[0] != $primary_key) continue;
00373             if (!isset($indexes[$row[0]])) {
00374                                 $indexes[$row[0]] = array(
00375                                    'unique' => ($row[1] == 'UNIQUE'),
00376                                    'columns' => array()
00377                                 );
00378             }
00379             $indexes[$row[0]]['columns'][$row[2] - 1] = $row[3];
00380         }
00381 
00382         // sort columns by order in the index
00383         foreach ( array_keys ($indexes) as $index ) {
00384             ksort ($indexes[$index]['columns']);
00385         }
00386 
00387                 if (isset($savem)) { 
00388             $this->SetFetchMode($savem);
00389                         $ADODB_FETCH_MODE = $save;
00390                 }
00391         return $indexes;
00392         }
00393         
00394         function BeginTrans()
00395         {       
00396                 if ($this->transOff) return true;
00397                 $this->transCnt += 1;
00398                 $this->autoCommit = false;
00399                 $this->_commit = OCI_DEFAULT;
00400                 
00401                 if ($this->_transmode) $this->Execute("SET TRANSACTION ".$this->_transmode);
00402                 return true;
00403         }
00404         
00405         function CommitTrans($ok=true) 
00406         { 
00407                 if ($this->transOff) return true;
00408                 if (!$ok) return $this->RollbackTrans();
00409                 
00410                 if ($this->transCnt) $this->transCnt -= 1;
00411                 $ret = OCIcommit($this->_connectionID);
00412                 $this->_commit = OCI_COMMIT_ON_SUCCESS;
00413                 $this->autoCommit = true;
00414                 return $ret;
00415         }
00416         
00417         function RollbackTrans()
00418         {
00419                 if ($this->transOff) return true;
00420                 if ($this->transCnt) $this->transCnt -= 1;
00421                 $ret = OCIrollback($this->_connectionID);
00422                 $this->_commit = OCI_COMMIT_ON_SUCCESS;
00423                 $this->autoCommit = true;
00424                 return $ret;
00425         }
00426         
00427         
00428         function SelectDB($dbName) 
00429         {
00430                 return false;
00431         }
00432 
00433         function ErrorMsg() 
00434         {
00435                 if ($this->_errorMsg !== false) return $this->_errorMsg;
00436 
00437                 if (is_resource($this->_stmt)) $arr = @OCIError($this->_stmt);
00438                 if (empty($arr)) {
00439                         if (is_resource($this->_connectionID)) $arr = @OCIError($this->_connectionID);
00440                         else $arr = @OCIError();
00441                         if ($arr === false) return '';
00442                 }
00443                 $this->_errorMsg = $arr['message'];
00444                 $this->_errorCode = $arr['code'];
00445                 return $this->_errorMsg;
00446         }
00447 
00448         function ErrorNo() 
00449         {
00450                 if ($this->_errorCode !== false) return $this->_errorCode;
00451                 
00452                 if (is_resource($this->_stmt)) $arr = @OCIError($this->_stmt);
00453                 if (empty($arr)) {
00454                         $arr = @OCIError($this->_connectionID);
00455                         if ($arr == false) $arr = @OCIError();
00456                         if ($arr == false) return '';
00457                 }
00458                 
00459                 $this->_errorMsg = $arr['message'];
00460                 $this->_errorCode = $arr['code'];
00461                 
00462                 return $arr['code'];
00463         }
00464         
00465         // Format date column in sql string given an input format that understands Y M D
00466         function SQLDate($fmt, $col=false)
00467         {       
00468                 if (!$col) $col = $this->sysTimeStamp;
00469                 $s = 'TO_CHAR('.$col.",'";
00470                 
00471                 $len = strlen($fmt);
00472                 for ($i=0; $i < $len; $i++) {
00473                         $ch = $fmt[$i];
00474                         switch($ch) {
00475                         case 'Y':
00476                         case 'y':
00477                                 $s .= 'YYYY';
00478                                 break;
00479                         case 'Q':
00480                         case 'q':
00481                                 $s .= 'Q';
00482                                 break;
00483                                 
00484                         case 'M':
00485                                 $s .= 'Mon';
00486                                 break;
00487                                 
00488                         case 'm':
00489                                 $s .= 'MM';
00490                                 break;
00491                         case 'D':
00492                         case 'd':
00493                                 $s .= 'DD';
00494                                 break;
00495                         
00496                         case 'H':
00497                                 $s.= 'HH24';
00498                                 break;
00499                                 
00500                         case 'h':
00501                                 $s .= 'HH';
00502                                 break;
00503                                 
00504                         case 'i':
00505                                 $s .= 'MI';
00506                                 break;
00507                         
00508                         case 's':
00509                                 $s .= 'SS';
00510                                 break;
00511                         
00512                         case 'a':
00513                         case 'A':
00514                                 $s .= 'AM';
00515                                 break;
00516                                 
00517                         case 'w':
00518                                 $s .= 'D';
00519                                 break;
00520                                 
00521                         case 'l':
00522                                 $s .= 'DAY';
00523                                 break;
00524                                 
00525                          case 'W':
00526                                 $s .= 'WW';
00527                                 break;
00528                                 
00529                         default:
00530                         // handle escape characters...
00531                                 if ($ch == '\\') {
00532                                         $i++;
00533                                         $ch = substr($fmt,$i,1);
00534                                 }
00535                                 if (strpos('-/.:;, ',$ch) !== false) $s .= $ch;
00536                                 else $s .= '"'.$ch.'"';
00537                                 
00538                         }
00539                 }
00540                 return $s. "')";
00541         }
00542         
00543         
00544         /*
00545         This algorithm makes use of
00546         
00547         a. FIRST_ROWS hint
00548         The FIRST_ROWS hint explicitly chooses the approach to optimize response time, 
00549         that is, minimum resource usage to return the first row. Results will be returned 
00550         as soon as they are identified. 
00551 
00552         b. Uses rownum tricks to obtain only the required rows from a given offset.
00553          As this uses complicated sql statements, we only use this if the $offset >= 100. 
00554          This idea by Tomas V V Cox.
00555          
00556          This implementation does not appear to work with oracle 8.0.5 or earlier. Comment
00557          out this function then, and the slower SelectLimit() in the base class will be used.
00558         */
00559         function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
00560         {
00561                 // seems that oracle only supports 1 hint comment in 8i
00562                 if ($this->firstrows) {
00563                         if (strpos($sql,'/*+') !== false)
00564                                 $sql = str_replace('/*+ ','/*+FIRST_ROWS ',$sql);
00565                         else
00566                                 $sql = preg_replace('/^[ \t\n]*select/i','SELECT /*+FIRST_ROWS*/',$sql);
00567                 }
00568                 
00569                 if ($offset < $this->selectOffsetAlg1) {
00570                         if ($nrows > 0) {       
00571                                 if ($offset > 0) $nrows += $offset;
00572                                 //$inputarr['adodb_rownum'] = $nrows;
00573                                 if ($this->databaseType == 'oci8po') {
00574                                         $sql = "select * from (".$sql.") where rownum <= ?";
00575                                 } else {
00576                                         $sql = "select * from (".$sql.") where rownum <= :adodb_offset";
00577                                 } 
00578                                 $inputarr['adodb_offset'] = $nrows;
00579                                 $nrows = -1;
00580                         }
00581                         // note that $nrows = 0 still has to work ==> no rows returned
00582 
00583                         $rs =& ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
00584                         return $rs;
00585                         
00586                 } else {
00587                          // Algorithm by Tomas V V Cox, from PEAR DB oci8.php
00588                         
00589                          // Let Oracle return the name of the columns
00590                         $q_fields = "SELECT * FROM (".$sql.") WHERE NULL = NULL";
00591                          
00592                         $false = false;
00593                         if (! $stmt_arr = $this->Prepare($q_fields)) {
00594                                 return $false;
00595                         }
00596                         $stmt = $stmt_arr[1];
00597                          
00598                          if (is_array($inputarr)) {
00599                                 foreach($inputarr as $k => $v) {
00600                                         if (is_array($v)) {
00601                                                 if (sizeof($v) == 2) // suggested by g.giunta@libero.
00602                                                         OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]);
00603                                                 else
00604                                                         OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
00605                                         } else {
00606                                                 $len = -1;
00607                                                 if ($v === ' ') $len = 1;
00608                                                 if (isset($bindarr)) {  // is prepared sql, so no need to ocibindbyname again
00609                                                         $bindarr[$k] = $v;
00610                                                 } else {                                // dynamic sql, so rebind every time
00611                                                         OCIBindByName($stmt,":$k",$inputarr[$k],$len);
00612                                                 }
00613                                         }
00614                                 }
00615                         }
00616                         
00617                          if (!OCIExecute($stmt, OCI_DEFAULT)) {
00618                                  OCIFreeStatement($stmt); 
00619                                  return $false;
00620                          }
00621                          
00622                          $ncols = OCINumCols($stmt);
00623                          for ( $i = 1; $i <= $ncols; $i++ ) {
00624                                  $cols[] = '"'.OCIColumnName($stmt, $i).'"';
00625                          }
00626                          $result = false;
00627                         
00628                          OCIFreeStatement($stmt); 
00629                          $fields = implode(',', $cols);
00630                          $nrows += $offset;
00631                          $offset += 1; // in Oracle rownum starts at 1
00632                         
00633                         if ($this->databaseType == 'oci8po') {
00634                                          $sql = "SELECT $fields FROM".
00635                                           "(SELECT rownum as adodb_rownum, $fields FROM".
00636                                           " ($sql) WHERE rownum <= ?".
00637                                           ") WHERE adodb_rownum >= ?";
00638                                 } else {
00639                                          $sql = "SELECT $fields FROM".
00640                                           "(SELECT rownum as adodb_rownum, $fields FROM".
00641                                           " ($sql) WHERE rownum <= :adodb_nrows".
00642                                           ") WHERE adodb_rownum >= :adodb_offset";
00643                                 } 
00644                                 $inputarr['adodb_nrows'] = $nrows;
00645                                 $inputarr['adodb_offset'] = $offset;
00646                                 
00647                         if ($secs2cache>0) $rs =& $this->CacheExecute($secs2cache, $sql,$inputarr);
00648                         else $rs =& $this->Execute($sql,$inputarr);
00649                         return $rs;
00650                 }
00651         
00652         }
00653         
00674         function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
00675         {
00676                 
00677                 //if (strlen($val) < 4000) return $this->Execute("UPDATE $table SET $column=:blob WHERE $where",array('blob'=>$val)) != false;
00678                 
00679                 switch(strtoupper($blobtype)) {
00680                 default: ADOConnection::outp("<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
00681                 case 'BLOB': $type = OCI_B_BLOB; break;
00682                 case 'CLOB': $type = OCI_B_CLOB; break;
00683                 }
00684                 
00685                 if ($this->databaseType == 'oci8po') 
00686                         $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
00687                 else 
00688                         $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
00689                 
00690                 $desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB);
00691                 $arr['blob'] = array($desc,-1,$type);
00692                 if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT');
00693                 $commit = $this->autoCommit;
00694                 if ($commit) $this->BeginTrans();
00695                 $rs = $this->_Execute($sql,$arr);
00696                 if ($rez = !empty($rs)) $desc->save($val);
00697                 $desc->free();
00698                 if ($commit) $this->CommitTrans();
00699                 if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=FORCE');
00700                 
00701                 if ($rez) $rs->Close();
00702                 return $rez;
00703         }
00704         
00708         function UpdateBlobFile($table,$column,$val,$where,$blobtype='BLOB')
00709         {
00710                 switch(strtoupper($blobtype)) {
00711                 default: ADOConnection::outp( "<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
00712                 case 'BLOB': $type = OCI_B_BLOB; break;
00713                 case 'CLOB': $type = OCI_B_CLOB; break;
00714                 }
00715                 
00716                 if ($this->databaseType == 'oci8po') 
00717                         $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
00718                 else 
00719                         $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
00720                 
00721                 $desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB);
00722                 $arr['blob'] = array($desc,-1,$type);
00723                 
00724                 $this->BeginTrans();
00725                 $rs = ADODB_oci8::Execute($sql,$arr);
00726                 if ($rez = !empty($rs)) $desc->savefile($val);
00727                 $desc->free();
00728                 $this->CommitTrans();
00729                 
00730                 if ($rez) $rs->Close();
00731                 return $rez;
00732         }
00733 
00741         function &Execute($sql,$inputarr=false) 
00742         {
00743                 if ($this->fnExecute) {
00744                         $fn = $this->fnExecute;
00745                         $ret =& $fn($this,$sql,$inputarr);
00746                         if (isset($ret)) return $ret;
00747                 }
00748                 if ($inputarr) {
00749                         #if (!is_array($inputarr)) $inputarr = array($inputarr);
00750                         
00751                         $element0 = reset($inputarr);
00752                         
00753                         # is_object check because oci8 descriptors can be passed in
00754                         if (is_array($element0) && !is_object(reset($element0))) {
00755                                 if (is_string($sql))
00756                                         $stmt = $this->Prepare($sql);
00757                                 else
00758                                         $stmt = $sql;
00759                                         
00760                                 foreach($inputarr as $arr) {
00761                                         $ret =& $this->_Execute($stmt,$arr);
00762                                         if (!$ret) return $ret;
00763                                 }
00764                         } else {
00765                                 $ret =& $this->_Execute($sql,$inputarr);
00766                         }
00767                         
00768                 } else {
00769                         $ret =& $this->_Execute($sql,false);
00770                 }
00771 
00772                 return $ret;
00773         }
00774         
00775         /*
00776                 Example of usage:
00777                 
00778                 $stmt = $this->Prepare('insert into emp (empno, ename) values (:empno, :ename)');
00779         */
00780         function Prepare($sql,$cursor=false)
00781         {
00782         static $BINDNUM = 0;
00783         
00784                 $stmt = OCIParse($this->_connectionID,$sql);
00785 
00786                 if (!$stmt) {
00787                         $this->_errorMsg = false;
00788                         $this->_errorCode = false;
00789                         $arr = @OCIError($this->_connectionID);
00790                         if ($arr === false) return false;
00791                 
00792                         $this->_errorMsg = $arr['message'];
00793                         $this->_errorCode = $arr['code'];
00794                         return false;
00795                 }
00796                 
00797                 $BINDNUM += 1;
00798                 
00799                 $sttype = @OCIStatementType($stmt);
00800                 if ($sttype == 'BEGIN' || $sttype == 'DECLARE') {
00801                         return array($sql,$stmt,0,$BINDNUM, ($cursor) ? OCINewCursor($this->_connectionID) : false);
00802                 }
00803                 return array($sql,$stmt,0,$BINDNUM);
00804         }
00805         
00806         /*
00807                 Call an oracle stored procedure and returns a cursor variable as a recordset. 
00808                 Concept by Robert Tuttle robert@ud.com
00809                 
00810                 Example:
00811                         Note: we return a cursor variable in :RS2
00812                         $rs = $db->ExecuteCursor("BEGIN adodb.open_tab(:RS2); END;",'RS2');
00813                         
00814                         $rs = $db->ExecuteCursor(
00815                                 "BEGIN :RS2 = adodb.getdata(:VAR1); END;", 
00816                                 'RS2',
00817                                 array('VAR1' => 'Mr Bean'));
00818                         
00819         */
00820         function &ExecuteCursor($sql,$cursorName='rs',$params=false)
00821         {
00822                 if (is_array($sql)) $stmt = $sql;
00823                 else $stmt = ADODB_oci8::Prepare($sql,true); # true to allocate OCINewCursor
00824         
00825                 if (is_array($stmt) && sizeof($stmt) >= 5) {
00826                         $hasref = true;
00827                         $ignoreCur = false;
00828                         $this->Parameter($stmt, $ignoreCur, $cursorName, false, -1, OCI_B_CURSOR);
00829                         if ($params) {
00830                                 foreach($params as $k => $v) {
00831                                         $this->Parameter($stmt,$params[$k], $k);
00832                                 }
00833                         }
00834                 } else
00835                         $hasref = false;
00836                         
00837                 $rs =& $this->Execute($stmt);
00838                 if ($rs) {
00839                         if ($rs->databaseType == 'array') OCIFreeCursor($stmt[4]);
00840                         else if ($hasref) $rs->_refcursor = $stmt[4];
00841                 }
00842                 return $rs;
00843         }
00844         
00845         /*
00846                 Bind a variable -- very, very fast for executing repeated statements in oracle. 
00847                 Better than using
00848                         for ($i = 0; $i < $max; $i++) { 
00849                                 $p1 = ?; $p2 = ?; $p3 = ?;
00850                                 $this->Execute("insert into table (col0, col1, col2) values (:0, :1, :2)", 
00851                                         array($p1,$p2,$p3));
00852                         }
00853                 
00854                 Usage:
00855                         $stmt = $DB->Prepare("insert into table (col0, col1, col2) values (:0, :1, :2)");
00856                         $DB->Bind($stmt, $p1);
00857                         $DB->Bind($stmt, $p2);
00858                         $DB->Bind($stmt, $p3);
00859                         for ($i = 0; $i < $max; $i++) { 
00860                                 $p1 = ?; $p2 = ?; $p3 = ?;
00861                                 $DB->Execute($stmt);
00862                         }
00863                         
00864                 Some timings:           
00865                         ** Test table has 3 cols, and 1 index. Test to insert 1000 records
00866                         Time 0.6081s (1644.60 inserts/sec) with direct OCIParse/OCIExecute
00867                         Time 0.6341s (1577.16 inserts/sec) with ADOdb Prepare/Bind/Execute
00868                         Time 1.5533s ( 643.77 inserts/sec) with pure SQL using Execute
00869                         
00870                 Now if PHP only had batch/bulk updating like Java or PL/SQL...
00871         
00872                 Note that the order of parameters differs from OCIBindByName,
00873                 because we default the names to :0, :1, :2
00874         */
00875         function Bind(&$stmt,&$var,$size=4000,$type=false,$name=false,$isOutput=false)
00876         {
00877                 
00878                 if (!is_array($stmt)) return false;
00879         
00880         if (($type == OCI_B_CURSOR) && sizeof($stmt) >= 5) { 
00881             return OCIBindByName($stmt[1],":".$name,$stmt[4],$size,$type);
00882         }
00883         
00884                 if ($name == false) {
00885                         if ($type !== false) $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size,$type);
00886                         else $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size); // +1 byte for null terminator
00887                         $stmt[2] += 1;
00888                 } else if (oci_lob_desc($type)) {
00889                         if ($this->debug) {
00890                                 ADOConnection::outp("<b>Bind</b>: name = $name");
00891                         }
00892             //we have to create a new Descriptor here
00893                         $numlob = count($this->_refLOBs);
00894                 $this->_refLOBs[$numlob]['LOB'] = OCINewDescriptor($this->_connectionID, oci_lob_desc($type));
00895                         $this->_refLOBs[$numlob]['TYPE'] = $isOutput;
00896                         
00897                         $tmp = &$this->_refLOBs[$numlob]['LOB'];
00898                 $rez = OCIBindByName($stmt[1], ":".$name, $tmp, -1, $type);
00899                         if ($this->debug) {
00900                                 ADOConnection::outp("<b>Bind</b>: descriptor has been allocated, var (".$name.") binded");
00901                         }
00902                         
00903                         // if type is input then write data to lob now
00904                         if ($isOutput == false) {
00905                                 $var = $this->BlobEncode($var);
00906                                 $tmp->WriteTemporary($var);
00907                                 $this->_refLOBs[$numlob]['VAR'] = &$var;
00908                                 if ($this->debug) {
00909                                         ADOConnection::outp("<b>Bind</b>: LOB has been written to temp");
00910                                 }
00911                         } else {
00912                                 $this->_refLOBs[$numlob]['VAR'] = &$var;
00913                         }
00914                         $rez = $tmp;
00915                 } else {
00916                         if ($this->debug) 
00917                                 ADOConnection::outp("<b>Bind</b>: name = $name");
00918                         
00919                         if ($type !== false) $rez = OCIBindByName($stmt[1],":".$name,$var,$size,$type);
00920                         else $rez = OCIBindByName($stmt[1],":".$name,$var,$size); // +1 byte for null terminator
00921                 }
00922                 
00923                 return $rez;
00924         }
00925         
00926         function Param($name,$type=false)
00927         {
00928                 return ':'.$name;
00929         }
00930         
00931         /* 
00932         Usage:
00933                 $stmt = $db->Prepare('select * from table where id =:myid and group=:group');
00934                 $db->Parameter($stmt,$id,'myid');
00935                 $db->Parameter($stmt,$group,'group');
00936                 $db->Execute($stmt);
00937                 
00938                 @param $stmt Statement returned by Prepare() or PrepareSP().
00939                 @param $var PHP variable to bind to
00940                 @param $name Name of stored procedure variable name to bind to.
00941                 @param [$isOutput] Indicates direction of parameter 0/false=IN  1=OUT  2= IN/OUT. This is ignored in oci8.
00942                 @param [$maxLen] Holds an maximum length of the variable.
00943                 @param [$type] The data type of $var. Legal values depend on driver.
00944                 
00945                 See OCIBindByName documentation at php.net.
00946         */
00947         function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
00948         {
00949                         if  ($this->debug) {
00950                                 $prefix = ($isOutput) ? 'Out' : 'In';
00951                                 $ztype = (empty($type)) ? 'false' : $type;
00952                                 ADOConnection::outp( "{$prefix}Parameter(\$stmt, \$php_var='$var', \$name='$name', \$maxLen=$maxLen, \$type=$ztype);");
00953                         }
00954                         return $this->Bind($stmt,$var,$maxLen,$type,$name,$isOutput);
00955         }
00956         
00957         /*
00958         returns query ID if successful, otherwise false
00959         this version supports:
00960         
00961            1. $db->execute('select * from table');
00962            
00963            2. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
00964                   $db->execute($prepared_statement, array(1,2,3));
00965                   
00966            3. $db->execute('insert into table (a,b,c) values (:a,:b,:c)',array('a'=>1,'b'=>2,'c'=>3));
00967            
00968            4. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
00969                   $db->bind($stmt,1); $db->bind($stmt,2); $db->bind($stmt,3); 
00970                   $db->execute($stmt);
00971         */ 
00972         function _query($sql,$inputarr)
00973         {
00974                 if (is_array($sql)) { // is prepared sql
00975                         $stmt = $sql[1];
00976                         
00977                         // we try to bind to permanent array, so that OCIBindByName is persistent
00978                         // and carried out once only - note that max array element size is 4000 chars
00979                         if (is_array($inputarr)) {
00980                                 $bindpos = $sql[3];
00981                                 if (isset($this->_bind[$bindpos])) {
00982                                 // all tied up already
00983                                         $bindarr = &$this->_bind[$bindpos];
00984                                 } else {
00985                                 // one statement to bind them all
00986                                         $bindarr = array();
00987                                         foreach($inputarr as $k => $v) {
00988                                                 $bindarr[$k] = $v;
00989                                                 OCIBindByName($stmt,":$k",$bindarr[$k],is_string($v) && strlen($v)>4000 ? -1 : 4000);
00990                                         }
00991                                         $this->_bind[$bindpos] = &$bindarr;
00992                                 }
00993                         }
00994                 } else {
00995                         $stmt=OCIParse($this->_connectionID,$sql);
00996                 }
00997                         
00998                 $this->_stmt = $stmt;
00999                 if (!$stmt) return false;
01000         
01001                 if (defined('ADODB_PREFETCH_ROWS')) @OCISetPrefetch($stmt,ADODB_PREFETCH_ROWS);
01002                         
01003                 if (is_array($inputarr)) {
01004                         foreach($inputarr as $k => $v) {
01005                                 if (is_array($v)) {
01006                                         if (sizeof($v) == 2) // suggested by g.giunta@libero.
01007                                                 OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]);
01008                                         else
01009                                                 OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
01010                                         
01011                                         if ($this->debug==99) echo "name=:$k",' var='.$inputarr[$k][0],' len='.$v[1],' type='.$v[2],'<br>';
01012                                 } else {
01013                                         $len = -1;
01014                                         if ($v === ' ') $len = 1;
01015                                         if (isset($bindarr)) {  // is prepared sql, so no need to ocibindbyname again
01016                                                 $bindarr[$k] = $v;
01017                                         } else {                                // dynamic sql, so rebind every time
01018                                                 OCIBindByName($stmt,":$k",$inputarr[$k],$len);
01019                                         }
01020                                 }
01021                         }
01022                 }
01023                 
01024         $this->_errorMsg = false;
01025                 $this->_errorCode = false;
01026                 if (OCIExecute($stmt,$this->_commit)) {
01027 //OCIInternalDebug(1);                  
01028                         if (count($this -> _refLOBs) > 0) {
01029                 
01030                                 foreach ($this -> _refLOBs as $key => $value) {
01031                                         if ($this -> _refLOBs[$key]['TYPE'] == true) {
01032                                                 $tmp = $this -> _refLOBs[$key]['LOB'] -> load();
01033                                                 if ($this -> debug) {
01034                                                         ADOConnection::outp("<b>OUT LOB</b>: LOB has been loaded. <br>");
01035                                                 }
01036                                                 //$_GLOBALS[$this -> _refLOBs[$key]['VAR']] = $tmp;
01037                                                 $this -> _refLOBs[$key]['VAR'] = $tmp;
01038                                         } else {
01039                         $this->_refLOBs[$key]['LOB']->save($this->_refLOBs[$key]['VAR']);
01040                                                 $this -> _refLOBs[$key]['LOB']->free();
01041                                                 unset($this -> _refLOBs[$key]);
01042                         if ($this->debug) {
01043                                                         ADOConnection::outp("<b>IN LOB</b>: LOB has been saved. <br>");
01044                                                 }
01045                     }                                   
01046                                 }
01047                         }
01048                 
01049             switch (@OCIStatementType($stmt)) {
01050                 case "SELECT":
01051                                         return $stmt;
01052                                 
01053                                 case 'DECLARE':
01054                 case "BEGIN":
01055                     if (is_array($sql) && !empty($sql[4])) {
01056                                                 $cursor = $sql[4];
01057                                                 if (is_resource($cursor)) {
01058                                                         $ok = OCIExecute($cursor);      
01059                                 return $cursor;
01060                                                 }
01061                                                 return $stmt;
01062                     } else {
01063                                                 if (is_resource($stmt)) {
01064                                                         OCIFreeStatement($stmt);
01065                                                         return true;
01066                                                 }
01067                         return $stmt;
01068                     }
01069                     break;
01070                 default :
01071                                         // ociclose -- no because it could be used in a LOB?
01072                     return true;
01073             }
01074                 }
01075                 return false;
01076         }
01077         
01078         // returns true or false
01079         function _close()
01080         {
01081                 if (!$this->_connectionID) return;
01082                 
01083                 if (!$this->autoCommit) OCIRollback($this->_connectionID);
01084                 if (count($this->_refLOBs) > 0) {
01085                         foreach ($this ->_refLOBs as $key => $value) {
01086                                 $this->_refLOBs[$key]['LOB']->free();
01087                                 unset($this->_refLOBs[$key]);
01088                         }
01089                 }
01090                 OCILogoff($this->_connectionID);
01091                 
01092                 $this->_stmt = false;
01093                 $this->_connectionID = false;
01094         }
01095         
01096         function MetaPrimaryKeys($table, $owner=false,$internalKey=false)
01097         {
01098                 if ($internalKey) return array('ROWID');
01099                 
01100         // tested with oracle 8.1.7
01101                 $table = strtoupper($table);
01102                 if ($owner) {
01103                         $owner_clause = "AND ((a.OWNER = b.OWNER) AND (a.OWNER = UPPER('$owner')))";
01104                         $ptab = 'ALL_';
01105                 } else {
01106                         $owner_clause = '';
01107                         $ptab = 'USER_';
01108                 }
01109                 $sql = "
01110 SELECT /*+ RULE */ distinct b.column_name
01111    FROM {$ptab}CONSTRAINTS a
01112           , {$ptab}CONS_COLUMNS b
01113   WHERE ( UPPER(b.table_name) = ('$table'))
01114         AND (UPPER(a.table_name) = ('$table') and a.constraint_type = 'P')
01115         $owner_clause
01116         AND (a.constraint_name = b.constraint_name)";
01117 
01118                 $rs = $this->Execute($sql);
01119                 if ($rs && !$rs->EOF) {
01120                         $arr =& $rs->GetArray();
01121                         $a = array();
01122                         foreach($arr as $v) {
01123                                 $a[] = reset($v);
01124                         }
01125                         return $a;
01126                 }
01127                 else return false;
01128         }
01129         
01130         // http://gis.mit.edu/classes/11.521/sqlnotes/referential_integrity.html
01131         function MetaForeignKeys($table, $owner=false)
01132         {
01133         global $ADODB_FETCH_MODE;
01134         
01135                 $save = $ADODB_FETCH_MODE;
01136                 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
01137                 $table = $this->qstr(strtoupper($table));
01138                 if (!$owner) {
01139                         $owner = $this->user;
01140                         $tabp = 'user_';
01141                 } else
01142                         $tabp = 'all_';
01143                         
01144                 $owner = ' and owner='.$this->qstr(strtoupper($owner));
01145                 
01146                 $sql = 
01147 "select constraint_name,r_owner,r_constraint_name 
01148         from {$tabp}constraints
01149         where constraint_type = 'R' and table_name = $table $owner";
01150                 
01151                 $constraints =& $this->GetArray($sql);
01152                 $arr = false;
01153                 foreach($constraints as $constr) {
01154                         $cons = $this->qstr($constr[0]);
01155                         $rowner = $this->qstr($constr[1]);
01156                         $rcons = $this->qstr($constr[2]);
01157                         $cols = $this->GetArray("select column_name from {$tabp}cons_columns where constraint_name=$cons $owner order by position");
01158                         $tabcol = $this->GetArray("select table_name,column_name from {$tabp}cons_columns where owner=$rowner and constraint_name=$rcons order by position");
01159                         
01160                         if ($cols && $tabcol) 
01161                                 for ($i=0, $max=sizeof($co