source: trunk/phpgwapi/inc/adodb/drivers/adodb-oci8.inc.php @ 34

Revision 34, 42.3 KB checked in by niltonneto, 17 years ago (diff)

Versão nova do ADODB (4.5 para 4.95)

  • Property svn:eol-style set to native
  • Property svn:executable set to *
Line 
1<?php
2/*
3
4  version V4.94 23 Jan 2007 (c) 2000-2007 John Lim. All rights reserved.
5
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.
9
10  Latest version is available at http://adodb.sourceforge.net
11 
12  Code contributed by George Fourlanos <fou@infomap.gr>
13 
14  13 Nov 2000 jlim - removed all ora_* references.
15*/
16
17// security - hide paths
18if (!defined('ADODB_DIR')) die();
19
20/*
21NLS_Date_Format
22Allows you to use a date format other than the Oracle Lite default. When a literal
23character string appears where a date value is expected, the Oracle Lite database
24tests the string to see if it matches the formats of Oracle, SQL-92, or the value
25specified for this parameter in the POLITE.INI file. Setting this parameter also
26defines the default format used in the TO_CHAR or TO_DATE functions when no
27other format string is supplied.
28
29For Oracle the default is dd-mon-yy or dd-mon-yyyy, and for SQL-92 the default is
30yy-mm-dd or yyyy-mm-dd.
31
32Using 'RR' in the format forces two-digit years less than or equal to 49 to be
33interpreted as years in the 21st century (2000–2049), and years over 50 as years in
34the 20th century (1950–1999). Setting the RR format as the default for all two-digit
35year entries allows you to become year-2000 compliant. For example:
36NLS_DATE_FORMAT='RR-MM-DD'
37
38You can also modify the date format using the ALTER SESSION command.
39*/
40
41# define the LOB descriptor type for the given type
42# returns false if no LOB descriptor
43function oci_lob_desc($type) {
44        switch ($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;
51        }
52        return $result;
53}
54
55class 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";
63        var $_stmt;
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;
69        var $hasGenID = 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;
77        var $_bind = 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();
90       
91        // var $ansiOuter = true; // if oracle9
92   
93        function ADODB_oci8()
94        {
95                $this->_hasOCIFetchStatement = ADODB_PHPVER >= 0x4200;
96                if (defined('ADODB_EXTENSION')) $this->rsPrefix .= 'ext_';
97        }
98       
99        /*  Function &MetaColumns($table) added by smondino@users.sourceforge.net*/
100        function &MetaColumns($table)
101        {
102        global $ADODB_FETCH_MODE;
103       
104                $false = false;
105                $save = $ADODB_FETCH_MODE;
106                $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
107                if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
108               
109                $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table)));
110               
111                if (isset($savem)) $this->SetFetchMode($savem);
112                $ADODB_FETCH_MODE = $save;
113                if (!$rs) {
114                        return $false;
115                }
116                $retarr = array();
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];
126                }       
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];
130                       
131                        if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld;     
132                        else $retarr[strtoupper($fld->name)] = $fld;
133                        $rs->MoveNext();
134                }
135                $rs->Close();
136                if (empty($retarr))
137                        return  $false;
138                else
139                        return $retarr;
140        }
141       
142        function Time()
143        {
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));
146               
147                return false;
148        }
149 
150/*
151
152  Multiple modes of connection are supported:
153 
154  a. Local Database
155    $conn->Connect(false,'scott','tiger');
156 
157  b. From tnsnames.ora
158    $conn->Connect(false,'scott','tiger',$tnsname);
159    $conn->Connect($tnsname,'scott','tiger');
160 
161  c. Server + service name
162    $conn->Connect($serveraddress,'scott,'tiger',$service_name);
163 
164  d. Server + SID
165        $conn->connectSID = true;
166        $conn->Connect($serveraddress,'scott,'tiger',$SID);
167
168
169Example TNSName:
170---------------
171NATSOFT.DOMAIN =
172  (DESCRIPTION =
173        (ADDRESS_LIST =
174          (ADDRESS = (PROTOCOL = TCP)(HOST = kermit)(PORT = 1523))
175        )
176        (CONNECT_DATA =
177          (SERVICE_NAME = natsoft.domain)
178        )
179  )
180 
181  There are 3 connection modes, 0 = non-persistent, 1 = persistent, 2 = force new connection
182       
183*/
184        function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$mode=0)
185        {
186                if (!function_exists('OCIPLogon')) return null;
187               
188               
189        $this->_errorMsg = false;
190                $this->_errorCode = false;
191               
192                if($argHostname) { // added by Jorma Tuomainen <jorma.tuomainen@ppoy.fi>
193                        if (empty($argDatabasename)) $argDatabasename = $argHostname;
194                        else {
195                                if(strpos($argHostname,":")) {
196                                        $argHostinfo=explode(":",$argHostname);
197                                        $argHostname=$argHostinfo[0];
198                                        $argHostport=$argHostinfo[1];
199                                } else {
200                                        $argHostport = empty($this->port)?  "1521" : $this->port;
201                                }
202                               
203                                if ($this->connectSID) {
204                                        $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
205                                        .")(PORT=$argHostport))(CONNECT_DATA=(SID=$argDatabasename)))";
206                                } else
207                                        $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
208                                        .")(PORT=$argHostport))(CONNECT_DATA=(SERVICE_NAME=$argDatabasename)))";
209                        }
210                }
211                               
212                //if ($argHostname) print "<p>Connect: 1st argument should be left blank for $this->databaseType</p>";
213                if ($mode==1) {
214                        $this->_connectionID = ($this->charSet) ?
215                                OCIPLogon($argUsername,$argPassword, $argDatabasename,$this->charSet)
216                                :
217                                OCIPLogon($argUsername,$argPassword, $argDatabasename)
218                                ;
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)
223                                :
224                                OCINLogon($argUsername,$argPassword, $argDatabasename);
225                               
226                } else {
227                        $this->_connectionID = ($this->charSet) ?
228                                OCILogon($argUsername,$argPassword, $argDatabasename,$this->charSet)
229                                :
230                                OCILogon($argUsername,$argPassword, $argDatabasename);
231                }
232                if (!$this->_connectionID) return false;
233                if ($this->_initdate) {
234                        $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='".$this->NLS_DATE_FORMAT."'");
235                }
236               
237                // looks like:
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;
241                return true;
242        }
243       
244        function ServerInfo()
245        {
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']);
249                return $arr;
250        }
251                // returns true or false
252        function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
253        {
254                return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,1);
255        }
256       
257        // returns true or false
258        function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
259        {
260                return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,2);
261        }
262       
263        function _affectedrows()
264        {
265                if (is_resource($this->_stmt)) return @OCIRowCount($this->_stmt);
266                return 0;
267        }
268       
269        function IfNull( $field, $ifNull )
270        {
271                return " NVL($field, $ifNull) "; // if Oracle
272        }
273       
274        // format and return date string in database date format
275        function DBDate($d)
276        {
277                if (empty($d) && $d !== 0) return 'null';
278               
279                if (is_string($d)) $d = ADORecordSet::UnixDate($d);
280                return "TO_DATE(".adodb_date($this->fmtDate,$d).",'".$this->dateformat."')";
281        }
282
283        function BindDate($d)
284        {
285                $d = ADOConnection::DBDate($d);
286                if (strncmp($d,"'",1)) return $d;
287               
288                return substr($d,1,strlen($d)-2);
289        }
290       
291        function BindTimeStamp($d)
292        {
293                $d = ADOConnection::DBTimeStamp($d);
294                if (strncmp($d,"'",1)) return $d;
295               
296                return substr($d,1,strlen($d)-2);
297        }
298       
299        // format and return date string in database timestamp format
300        function DBTimeStamp($ts)
301        {
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')";
305        }
306       
307        function RowLock($tables,$where,$flds='1 as ignore')
308        {
309                if ($this->autoCommit) $this->BeginTrans();
310                return $this->GetOne("select $flds from $tables where $where for update");
311        }
312       
313        function &MetaTables($ttype=false,$showSchema=false,$mask=false)
314        {
315                if ($mask) {
316                        $save = $this->metaTablesSQL;
317                        $mask = $this->qstr(strtoupper($mask));
318                        $this->metaTablesSQL .= " AND upper(table_name) like $mask";
319                }
320                $ret =& ADOConnection::MetaTables($ttype,$showSchema);
321               
322                if ($mask) {
323                        $this->metaTablesSQL = $save;
324                }
325                return $ret;
326        }
327       
328        // Mark Newnham
329        function &MetaIndexes ($table, $primary = FALSE, $owner=false)
330        {
331        // save old fetch mode
332        global $ADODB_FETCH_MODE;
333
334        $save = $ADODB_FETCH_MODE;
335        $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
336
337        if ($this->fetchMode !== FALSE) {
338               $savem = $this->SetFetchMode(FALSE);
339        }
340
341                // get index details
342                $table = strtoupper($table);
343
344                // get Primary index
345                $primary_key = '';
346
347                $false = false;
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
351
352                if ($primary==TRUE && $primary_key=='') {
353                         if (isset($savem))
354                $this->SetFetchMode($savem);
355                        $ADODB_FETCH_MODE = $save;
356                        return $false; //There is no primary key
357                }
358
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));
360
361               
362        if (!is_object($rs)) {
363                        if (isset($savem))
364                                $this->SetFetchMode($savem);
365                        $ADODB_FETCH_MODE = $save;
366            return $false;
367        }
368
369                $indexes = array ();
370        // parse index data into array
371
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'),
377                                   'columns' => array()
378                                );
379            }
380            $indexes[$row[0]]['columns'][$row[2] - 1] = $row[3];
381        }
382
383        // sort columns by order in the index
384        foreach ( array_keys ($indexes) as $index ) {
385            ksort ($indexes[$index]['columns']);
386        }
387
388                if (isset($savem)) {
389            $this->SetFetchMode($savem);
390                        $ADODB_FETCH_MODE = $save;
391                }
392        return $indexes;
393        }
394       
395        function BeginTrans()
396        {       
397                if ($this->transOff) return true;
398                $this->transCnt += 1;
399                $this->autoCommit = false;
400                $this->_commit = OCI_DEFAULT;
401               
402                if ($this->_transmode) $this->Execute("SET TRANSACTION ".$this->_transmode);
403                return true;
404        }
405       
406        function CommitTrans($ok=true)
407        {
408                if ($this->transOff) return true;
409                if (!$ok) return $this->RollbackTrans();
410               
411                if ($this->transCnt) $this->transCnt -= 1;
412                $ret = OCIcommit($this->_connectionID);
413                $this->_commit = OCI_COMMIT_ON_SUCCESS;
414                $this->autoCommit = true;
415                return $ret;
416        }
417       
418        function RollbackTrans()
419        {
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;
425                return $ret;
426        }
427       
428       
429        function SelectDB($dbName)
430        {
431                return false;
432        }
433
434        function ErrorMsg()
435        {
436                if ($this->_errorMsg !== false) return $this->_errorMsg;
437
438                if (is_resource($this->_stmt)) $arr = @OCIError($this->_stmt);
439                if (empty($arr)) {
440                        if (is_resource($this->_connectionID)) $arr = @OCIError($this->_connectionID);
441                        else $arr = @OCIError();
442                        if ($arr === false) return '';
443                }
444                $this->_errorMsg = $arr['message'];
445                $this->_errorCode = $arr['code'];
446                return $this->_errorMsg;
447        }
448
449        function ErrorNo()
450        {
451                if ($this->_errorCode !== false) return $this->_errorCode;
452               
453                if (is_resource($this->_stmt)) $arr = @OCIError($this->_stmt);
454                if (empty($arr)) {
455                        $arr = @OCIError($this->_connectionID);
456                        if ($arr == false) $arr = @OCIError();
457                        if ($arr == false) return '';
458                }
459               
460                $this->_errorMsg = $arr['message'];
461                $this->_errorCode = $arr['code'];
462               
463                return $arr['code'];
464        }
465       
466        // Format date column in sql string given an input format that understands Y M D
467        function SQLDate($fmt, $col=false)
468        {       
469                if (!$col) $col = $this->sysTimeStamp;
470                $s = 'TO_CHAR('.$col.",'";
471               
472                $len = strlen($fmt);
473                for ($i=0; $i < $len; $i++) {
474                        $ch = $fmt[$i];
475                        switch($ch) {
476                        case 'Y':
477                        case 'y':
478                                $s .= 'YYYY';
479                                break;
480                        case 'Q':
481                        case 'q':
482                                $s .= 'Q';
483                                break;
484                               
485                        case 'M':
486                                $s .= 'Mon';
487                                break;
488                               
489                        case 'm':
490                                $s .= 'MM';
491                                break;
492                        case 'D':
493                        case 'd':
494                                $s .= 'DD';
495                                break;
496                       
497                        case 'H':
498                                $s.= 'HH24';
499                                break;
500                               
501                        case 'h':
502                                $s .= 'HH';
503                                break;
504                               
505                        case 'i':
506                                $s .= 'MI';
507                                break;
508                       
509                        case 's':
510                                $s .= 'SS';
511                                break;
512                       
513                        case 'a':
514                        case 'A':
515                                $s .= 'AM';
516                                break;
517                               
518                        case 'w':
519                                $s .= 'D';
520                                break;
521                               
522                        case 'l':
523                                $s .= 'DAY';
524                                break;
525                               
526                         case 'W':
527                                $s .= 'WW';
528                                break;
529                               
530                        default:
531                        // handle escape characters...
532                                if ($ch == '\\') {
533                                        $i++;
534                                        $ch = substr($fmt,$i,1);
535                                }
536                                if (strpos('-/.:;, ',$ch) !== false) $s .= $ch;
537                                else $s .= '"'.$ch.'"';
538                               
539                        }
540                }
541                return $s. "')";
542        }
543       
544       
545        /*
546        This algorithm makes use of
547       
548        a. FIRST_ROWS hint
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.
552
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.
556         
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.
559        */
560        function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
561        {
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);
566                        else
567                                $sql = preg_replace('/^[ \t\n]*select/i','SELECT /*+FIRST_ROWS*/',$sql);
568                }
569               
570                if ($offset < $this->selectOffsetAlg1) {
571                        if ($nrows > 0) {       
572                                if ($offset > 0) $nrows += $offset;
573                                //$inputarr['adodb_rownum'] = $nrows;
574                                if ($this->databaseType == 'oci8po') {
575                                        $sql = "select * from (".$sql.") where rownum <= ?";
576                                } else {
577                                        $sql = "select * from (".$sql.") where rownum <= :adodb_offset";
578                                }
579                                $inputarr['adodb_offset'] = $nrows;
580                                $nrows = -1;
581                        }
582                        // note that $nrows = 0 still has to work ==> no rows returned
583
584                        $rs =& ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
585                        return $rs;
586                       
587                } else {
588                         // Algorithm by Tomas V V Cox, from PEAR DB oci8.php
589                       
590                         // Let Oracle return the name of the columns
591                        $q_fields = "SELECT * FROM (".$sql.") WHERE NULL = NULL";
592                         
593                        $false = false;
594                        if (! $stmt_arr = $this->Prepare($q_fields)) {
595                                return $false;
596                        }
597                        $stmt = $stmt_arr[1];
598                         
599                         if (is_array($inputarr)) {
600                                foreach($inputarr as $k => $v) {
601                                        if (is_array($v)) {
602                                                if (sizeof($v) == 2) // suggested by g.giunta@libero.
603                                                        OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]);
604                                                else
605                                                        OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
606                                        } else {
607                                                $len = -1;
608                                                if ($v === ' ') $len = 1;
609                                                if (isset($bindarr)) {  // is prepared sql, so no need to ocibindbyname again
610                                                        $bindarr[$k] = $v;
611                                                } else {                                // dynamic sql, so rebind every time
612                                                        OCIBindByName($stmt,":$k",$inputarr[$k],$len);
613                                                }
614                                        }
615                                }
616                        }
617                       
618                         if (!OCIExecute($stmt, OCI_DEFAULT)) {
619                                 OCIFreeStatement($stmt);
620                                 return $false;
621                         }
622                         
623                         $ncols = OCINumCols($stmt);
624                         for ( $i = 1; $i <= $ncols; $i++ ) {
625                                 $cols[] = '"'.OCIColumnName($stmt, $i).'"';
626                         }
627                         $result = false;
628                       
629                         OCIFreeStatement($stmt);
630                         $fields = implode(',', $cols);
631                         $nrows += $offset;
632                         $offset += 1; // in Oracle rownum starts at 1
633                       
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 >= ?";
639                                } else {
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";
644                                }
645                                $inputarr['adodb_nrows'] = $nrows;
646                                $inputarr['adodb_offset'] = $offset;
647                               
648                        if ($secs2cache>0) $rs =& $this->CacheExecute($secs2cache, $sql,$inputarr);
649                        else $rs =& $this->Execute($sql,$inputarr);
650                        return $rs;
651                }
652       
653        }
654       
655        /**
656        * Usage:
657        * Store BLOBs and CLOBs
658        *
659        * Example: to store $var in a blob
660        *
661        *       $conn->Execute('insert into TABLE (id,ablob) values(12,empty_blob())');
662        *       $conn->UpdateBlob('TABLE', 'ablob', $varHoldingBlob, 'ID=12', 'BLOB');
663        *       
664        *       $blobtype supports 'BLOB' and 'CLOB', but you need to change to 'empty_clob()'.
665        *
666        *  to get length of LOB:
667        *       select DBMS_LOB.GETLENGTH(ablob) from TABLE
668        *
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...
673        */
674
675        function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
676        {
677               
678                //if (strlen($val) < 4000) return $this->Execute("UPDATE $table SET $column=:blob WHERE $where",array('blob'=>$val)) != false;
679               
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;
684                }
685               
686                if ($this->databaseType == 'oci8po')
687                        $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
688                else
689                        $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
690               
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);
698                $desc->free();
699                if ($commit) $this->CommitTrans();
700                if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=FORCE');
701               
702                if ($rez) $rs->Close();
703                return $rez;
704        }
705       
706        /**
707        * Usage:  store file pointed to by $var in a blob
708        */
709        function UpdateBlobFile($table,$column,$val,$where,$blobtype='BLOB')
710        {
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;
715                }
716               
717                if ($this->databaseType == 'oci8po')
718                        $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
719                else
720                        $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
721               
722                $desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB);
723                $arr['blob'] = array($desc,-1,$type);
724               
725                $this->BeginTrans();
726                $rs = ADODB_oci8::Execute($sql,$arr);
727                if ($rez = !empty($rs)) $desc->savefile($val);
728                $desc->free();
729                $this->CommitTrans();
730               
731                if ($rez) $rs->Close();
732                return $rez;
733        }
734
735                /**
736         * Execute SQL
737         *
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
741         */
742        function &Execute($sql,$inputarr=false)
743        {
744                if ($this->fnExecute) {
745                        $fn = $this->fnExecute;
746                        $ret =& $fn($this,$sql,$inputarr);
747                        if (isset($ret)) return $ret;
748                }
749                if ($inputarr) {
750                        #if (!is_array($inputarr)) $inputarr = array($inputarr);
751                       
752                        $element0 = reset($inputarr);
753                       
754                        # is_object check because oci8 descriptors can be passed in
755                        if (is_array($element0) && !is_object(reset($element0))) {
756                                if (is_string($sql))
757                                        $stmt = $this->Prepare($sql);
758                                else
759                                        $stmt = $sql;
760                                       
761                                foreach($inputarr as $arr) {
762                                        $ret =& $this->_Execute($stmt,$arr);
763                                        if (!$ret) return $ret;
764                                }
765                        } else {
766                                $ret =& $this->_Execute($sql,$inputarr);
767                        }
768                       
769                } else {
770                        $ret =& $this->_Execute($sql,false);
771                }
772
773                return $ret;
774        }
775       
776        /*
777                Example of usage:
778               
779                $stmt = $this->Prepare('insert into emp (empno, ename) values (:empno, :ename)');
780        */
781        function Prepare($sql,$cursor=false)
782        {
783        static $BINDNUM = 0;
784       
785                $stmt = OCIParse($this->_connectionID,$sql);
786
787                if (!$stmt) {
788                        $this->_errorMsg = false;
789                        $this->_errorCode = false;
790                        $arr = @OCIError($this->_connectionID);
791                        if ($arr === false) return false;
792               
793                        $this->_errorMsg = $arr['message'];
794                        $this->_errorCode = $arr['code'];
795                        return false;
796                }
797               
798                $BINDNUM += 1;
799               
800                $sttype = @OCIStatementType($stmt);
801                if ($sttype == 'BEGIN' || $sttype == 'DECLARE') {
802                        return array($sql,$stmt,0,$BINDNUM, ($cursor) ? OCINewCursor($this->_connectionID) : false);
803                }
804                return array($sql,$stmt,0,$BINDNUM);
805        }
806       
807        /*
808                Call an oracle stored procedure and returns a cursor variable as a recordset.
809                Concept by Robert Tuttle robert@ud.com
810               
811                Example:
812                        Note: we return a cursor variable in :RS2
813                        $rs = $db->ExecuteCursor("BEGIN adodb.open_tab(:RS2); END;",'RS2');
814                       
815                        $rs = $db->ExecuteCursor(
816                                "BEGIN :RS2 = adodb.getdata(:VAR1); END;",
817                                'RS2',
818                                array('VAR1' => 'Mr Bean'));
819                       
820        */
821        function &ExecuteCursor($sql,$cursorName='rs',$params=false)
822        {
823                if (is_array($sql)) $stmt = $sql;
824                else $stmt = ADODB_oci8::Prepare($sql,true); # true to allocate OCINewCursor
825       
826                if (is_array($stmt) && sizeof($stmt) >= 5) {
827                        $hasref = true;
828                        $ignoreCur = false;
829                        $this->Parameter($stmt, $ignoreCur, $cursorName, false, -1, OCI_B_CURSOR);
830                        if ($params) {
831                                foreach($params as $k => $v) {
832                                        $this->Parameter($stmt,$params[$k], $k);
833                                }
834                        }
835                } else
836                        $hasref = false;
837                       
838                $rs =& $this->Execute($stmt);
839                if ($rs) {
840                        if ($rs->databaseType == 'array') OCIFreeCursor($stmt[4]);
841                        else if ($hasref) $rs->_refcursor = $stmt[4];
842                }
843                return $rs;
844        }
845       
846        /*
847                Bind a variable -- very, very fast for executing repeated statements in oracle.
848                Better than using
849                        for ($i = 0; $i < $max; $i++) {
850                                $p1 = ?; $p2 = ?; $p3 = ?;
851                                $this->Execute("insert into table (col0, col1, col2) values (:0, :1, :2)",
852                                        array($p1,$p2,$p3));
853                        }
854               
855                Usage:
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 = ?;
862                                $DB->Execute($stmt);
863                        }
864                       
865                Some timings:           
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
870                       
871                Now if PHP only had batch/bulk updating like Java or PL/SQL...
872       
873                Note that the order of parameters differs from OCIBindByName,
874                because we default the names to :0, :1, :2
875        */
876        function Bind(&$stmt,&$var,$size=4000,$type=false,$name=false,$isOutput=false)
877        {
878               
879                if (!is_array($stmt)) return false;
880       
881        if (($type == OCI_B_CURSOR) && sizeof($stmt) >= 5) {
882            return OCIBindByName($stmt[1],":".$name,$stmt[4],$size,$type);
883        }
884       
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
888                        $stmt[2] += 1;
889                } else if (oci_lob_desc($type)) {
890                        if ($this->debug) {
891                                ADOConnection::outp("<b>Bind</b>: name = $name");
892                        }
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;
897                       
898                        $tmp = &$this->_refLOBs[$numlob]['LOB'];
899                $rez = OCIBindByName($stmt[1], ":".$name, $tmp, -1, $type);
900                        if ($this->debug) {
901                                ADOConnection::outp("<b>Bind</b>: descriptor has been allocated, var (".$name.") binded");
902                        }
903                       
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;
909                                if ($this->debug) {
910                                        ADOConnection::outp("<b>Bind</b>: LOB has been written to temp");
911                                }
912                        } else {
913                                $this->_refLOBs[$numlob]['VAR'] = &$var;
914                        }
915                        $rez = $tmp;
916                } else {
917                        if ($this->debug)
918                                ADOConnection::outp("<b>Bind</b>: name = $name");
919                       
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
922                }
923               
924                return $rez;
925        }
926       
927        function Param($name,$type=false)
928        {
929                return ':'.$name;
930        }
931       
932        /*
933        Usage:
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');
937                $db->Execute($stmt);
938               
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.
945               
946                See OCIBindByName documentation at php.net.
947        */
948        function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
949        {
950                        if  ($this->debug) {
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);");
954                        }
955                        return $this->Bind($stmt,$var,$maxLen,$type,$name,$isOutput);
956        }
957       
958        /*
959        returns query ID if successful, otherwise false
960        this version supports:
961       
962           1. $db->execute('select * from table');
963           
964           2. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
965                  $db->execute($prepared_statement, array(1,2,3));
966                 
967           3. $db->execute('insert into table (a,b,c) values (:a,:b,:c)',array('a'=>1,'b'=>2,'c'=>3));
968           
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);
971                  $db->execute($stmt);
972        */
973        function _query($sql,$inputarr)
974        {
975                if (is_array($sql)) { // is prepared sql
976                        $stmt = $sql[1];
977                       
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)) {
981                                $bindpos = $sql[3];
982                                if (isset($this->_bind[$bindpos])) {
983                                // all tied up already
984                                        $bindarr = &$this->_bind[$bindpos];
985                                } else {
986                                // one statement to bind them all
987                                        $bindarr = array();
988                                        foreach($inputarr as $k => $v) {
989                                                $bindarr[$k] = $v;
990                                                OCIBindByName($stmt,":$k",$bindarr[$k],is_string($v) && strlen($v)>4000 ? -1 : 4000);
991                                        }
992                                        $this->_bind[$bindpos] = &$bindarr;
993                                }
994                        }
995                } else {
996                        $stmt=OCIParse($this->_connectionID,$sql);
997                }
998                       
999                $this->_stmt = $stmt;
1000                if (!$stmt) return false;
1001       
1002                if (defined('ADODB_PREFETCH_ROWS')) @OCISetPrefetch($stmt,ADODB_PREFETCH_ROWS);
1003                       
1004                if (is_array($inputarr)) {
1005                        foreach($inputarr as $k => $v) {
1006                                if (is_array($v)) {
1007                                        if (sizeof($v) == 2) // suggested by g.giunta@libero.
1008                                                OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]);
1009                                        else
1010                                                OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
1011                                       
1012                                        if ($this->debug==99) echo "name=:$k",' var='.$inputarr[$k][0],' len='.$v[1],' type='.$v[2],'<br>';
1013                                } else {
1014                                        $len = -1;
1015                                        if ($v === ' ') $len = 1;
1016                                        if (isset($bindarr)) {  // is prepared sql, so no need to ocibindbyname again
1017                                                $bindarr[$k] = $v;
1018                                        } else {                                // dynamic sql, so rebind every time
1019                                                OCIBindByName($stmt,":$k",$inputarr[$k],$len);
1020                                        }
1021                                }
1022                        }
1023                }
1024               
1025        $this->_errorMsg = false;
1026                $this->_errorCode = false;
1027                if (OCIExecute($stmt,$this->_commit)) {
1028//OCIInternalDebug(1);                 
1029                        if (count($this -> _refLOBs) > 0) {
1030               
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>");
1036                                                }
1037                                                //$_GLOBALS[$this -> _refLOBs[$key]['VAR']] = $tmp;
1038                                                $this -> _refLOBs[$key]['VAR'] = $tmp;
1039                                        } else {
1040                        $this->_refLOBs[$key]['LOB']->save($this->_refLOBs[$key]['VAR']);
1041                                                $this -> _refLOBs[$key]['LOB']->free();
1042                                                unset($this -> _refLOBs[$key]);
1043                        if ($this->debug) {
1044                                                        ADOConnection::outp("<b>IN LOB</b>: LOB has been saved. <br>");
1045                                                }
1046                    }                                   
1047                                }
1048                        }
1049               
1050            switch (@OCIStatementType($stmt)) {
1051                case "SELECT":
1052                                        return $stmt;
1053                               
1054                                case 'DECLARE':
1055                case "BEGIN":
1056                    if (is_array($sql) && !empty($sql[4])) {
1057                                                $cursor = $sql[4];
1058                                                if (is_resource($cursor)) {
1059                                                        $ok = OCIExecute($cursor);     
1060                                return $cursor;
1061                                                }
1062                                                return $stmt;
1063                    } else {
1064                                                if (is_resource($stmt)) {
1065                                                        OCIFreeStatement($stmt);
1066                                                        return true;
1067                                                }
1068                        return $stmt;
1069                    }
1070                    break;
1071                default :
1072                                        // ociclose -- no because it could be used in a LOB?
1073                    return true;
1074            }
1075                }
1076                return false;
1077        }
1078       
1079        // returns true or false
1080        function _close()
1081        {
1082                if (!$this->_connectionID) return;
1083               
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]);
1089                        }
1090                }
1091                OCILogoff($this->_connectionID);
1092               
1093                $this->_stmt = false;
1094                $this->_connectionID = false;
1095        }
1096       
1097        function MetaPrimaryKeys($table, $owner=false,$internalKey=false)
1098        {
1099                if ($internalKey) return array('ROWID');
1100               
1101        // tested with oracle 8.1.7
1102                $table = strtoupper($table);
1103                if ($owner) {
1104                        $owner_clause = "AND ((a.OWNER = b.OWNER) AND (a.OWNER = UPPER('$owner')))";
1105                        $ptab = 'ALL_';
1106                } else {
1107                        $owner_clause = '';
1108                        $ptab = 'USER_';
1109                }
1110                $sql = "
1111SELECT /*+ 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')
1116        $owner_clause
1117        AND (a.constraint_name = b.constraint_name)";
1118
1119                $rs = $this->Execute($sql);
1120                if ($rs && !$rs->EOF) {
1121                        $arr =& $rs->GetArray();
1122                        $a = array();
1123                        foreach($arr as $v) {
1124                                $a[] = reset($v);
1125                        }
1126                        return $a;
1127                }
1128                else return false;
1129        }
1130       
1131        // http://gis.mit.edu/classes/11.521/sqlnotes/referential_integrity.html
1132        function MetaForeignKeys($table, $owner=false)
1133        {
1134        global $ADODB_FETCH_MODE;
1135       
1136                $save = $ADODB_FETCH_MODE;
1137                $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
1138                $table = $this->qstr(strtoupper($table));
1139                if (!$owner) {
1140                        $owner = $this->user;
1141                        $tabp = 'user_';
1142                } else
1143                        $tabp = 'all_';
1144                       
1145                $owner = ' and owner='.$this->qstr(strtoupper($owner));
1146               
1147                $sql =
1148"select constraint_name,r_owner,r_constraint_name
1149        from {$tabp}constraints
1150        where constraint_type = 'R' and table_name = $table $owner";
1151               
1152                $constraints =& $this->GetArray($sql);
1153                $arr = false;
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");
1160                       
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];
1164                                }
1165                }
1166                $ADODB_FETCH_MODE = $save;
1167               
1168                return $arr;
1169        }
1170
1171       
1172        function CharMax()
1173        {
1174                return 4000;
1175        }
1176       
1177        function TextMax()
1178        {
1179                return 4000;
1180        }
1181       
1182        /**
1183         * Quotes a string.
1184         * An example is  $db->qstr("Don't bother",magic_quotes_runtime());
1185         *
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.
1189         *
1190         * @return  quoted string to be sent back to database
1191         */
1192        function qstr($s,$magic_quotes=false)
1193        {       
1194                //$nofixquotes=false;
1195       
1196                if ($this->noNullStrings && strlen($s)==0)$s = ' ';
1197                if (!$magic_quotes) {   
1198                        if ($this->replaceQuote[0] == '\\'){
1199                                $s = str_replace('\\','\\\\',$s);
1200                        }
1201                        return  "'".str_replace("'",$this->replaceQuote,$s)."'";
1202                }
1203               
1204                // undo magic quotes for "
1205                $s = str_replace('\\"','"',$s);
1206               
1207                $s = str_replace('\\\\','\\',$s);
1208                return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
1209               
1210        }
1211       
1212}
1213
1214/*--------------------------------------------------------------------------------------
1215                 Class Name: Recordset
1216--------------------------------------------------------------------------------------*/
1217
1218class ADORecordset_oci8 extends ADORecordSet {
1219
1220        var $databaseType = 'oci8';
1221        var $bind=false;
1222        var $_fieldobjs;
1223       
1224        //var $_arr = false;
1225               
1226        function ADORecordset_oci8($queryID,$mode=false)
1227        {
1228                if ($mode === false) {
1229                        global $ADODB_FETCH_MODE;
1230                        $mode = $ADODB_FETCH_MODE;
1231                }
1232                switch ($mode)
1233                {
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:
1238                default:
1239                $this->fetchMode = OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
1240                }
1241               
1242                $this->adodbFetchMode = $mode;
1243                $this->_queryID = $queryID;
1244        }
1245
1246
1247        function Init()
1248        {
1249                if ($this->_inited) return;
1250               
1251                $this->_inited = true;
1252                if ($this->_queryID) {
1253                       
1254                        $this->_currentRow = 0;
1255                        @$this->_initrs();
1256                        $this->EOF = !$this->_fetch();
1257                       
1258                        /*
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);
1263                        */
1264                       
1265                        if (!is_array($this->fields)) {
1266                                $this->_numOfRows = 0;
1267                                $this->fields = array();
1268                        }
1269                } else {
1270                        $this->fields = array();
1271                        $this->_numOfRows = 0;
1272                        $this->_numOfFields = 0;
1273                        $this->EOF = true;
1274                }
1275        }
1276       
1277        function _initrs()
1278        {
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);
1285                }
1286        }
1287
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.            */
1292
1293        function &_FetchField($fieldOffset = -1)
1294        {
1295                $fld = new ADOFieldObject;
1296                $fieldOffset += 1;
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) ";
1305                }
1306                return $fld;
1307        }
1308       
1309        /* For some reason, OCIcolumnname fails when called after _initrs() so we cache it */
1310        function &FetchField($fieldOffset = -1)
1311        {
1312                return $this->_fieldobjs[$fieldOffset];
1313        }
1314       
1315       
1316        /*
1317        // 10% speedup to move MoveNext to child class
1318        function _MoveNext()
1319        {
1320        //global $ADODB_EXTENSION;if ($ADODB_EXTENSION) return @adodb_movenext($this);
1321               
1322                if ($this->EOF) return false;
1323               
1324                $this->_currentRow++;
1325                if(@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode))
1326                        return true;
1327                $this->EOF = true;
1328               
1329                return false;
1330        }       */
1331       
1332       
1333        function MoveNext()
1334        {
1335                if (@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) {
1336                        $this->_currentRow += 1;
1337                        return true;
1338                }
1339                if (!$this->EOF) {
1340                        $this->_currentRow += 1;
1341                        $this->EOF = true;
1342                }
1343                return false;
1344        }
1345       
1346        /*
1347        # does not work as first record is retrieved in _initrs(), so is not included in GetArray()
1348        function &GetArray($nRows = -1)
1349        {
1350        global $ADODB_OCI8_GETARRAY;
1351       
1352                if (true ||  !empty($ADODB_OCI8_GETARRAY)) {
1353                        # does not support $ADODB_ANSI_PADDING_OFF
1354       
1355                        //OCI_RETURN_NULLS and OCI_RETURN_LOBS is set by OCIfetchstatement
1356                        switch($this->adodbFetchMode) {
1357                        case ADODB_FETCH_NUM:
1358                       
1359                                $ncols = @OCIfetchstatement($this->_queryID, $results, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW+OCI_NUM);
1360                                $results = array_merge(array($this->fields),$results);
1361                                return $results;
1362                               
1363                        case ADODB_FETCH_ASSOC:
1364                                if (ADODB_ASSOC_CASE != 2 || $this->databaseType != 'oci8') break;
1365                               
1366                                $ncols = @OCIfetchstatement($this->_queryID, $assoc, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW);
1367                                $results =& array_merge(array($this->fields),$assoc);
1368                                return $results;
1369                       
1370                        default:
1371                                break;
1372                        }
1373                }
1374                       
1375                $results =& ADORecordSet::GetArray($nRows);
1376                return $results;
1377               
1378        } */
1379       
1380        /* Optimize SelectLimit() by using OCIFetch() instead of OCIFetchInto() */
1381        function &GetArrayLimit($nrows,$offset=-1)
1382        {
1383                if ($offset <= 0) {
1384                        $arr =& $this->GetArray($nrows);
1385                        return $arr;
1386                }
1387                $arr = array();
1388                for ($i=1; $i < $offset; $i++)
1389                        if (!@OCIFetch($this->_queryID)) return $arr;
1390                       
1391                if (!@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) return $arr;;
1392                $results = array();
1393                $cnt = 0;
1394                while (!$this->EOF && $nrows != $cnt) {
1395                        $results[$cnt++] = $this->fields;
1396                        $this->MoveNext();
1397                }
1398               
1399                return $results;
1400        }
1401
1402       
1403        /* Use associative array to get fields array */
1404        function Fields($colname)
1405        {
1406                if (!$this->bind) {
1407                        $this->bind = array();
1408                        for ($i=0; $i < $this->_numOfFields; $i++) {
1409                                $o = $this->FetchField($i);
1410                                $this->bind[strtoupper($o->name)] = $i;
1411                        }
1412                }
1413               
1414                 return $this->fields[$this->bind[strtoupper($colname)]];
1415        }
1416       
1417
1418
1419        function _seek($row)
1420        {
1421                return false;
1422        }
1423
1424        function _fetch()
1425        {
1426                return @OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode);
1427        }
1428
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.               */
1431
1432        function _close()
1433        {
1434                if ($this->connection->_stmt === $this->_queryID) $this->connection->_stmt = false;
1435                if (!empty($this->_refcursor)) {
1436                        OCIFreeCursor($this->_refcursor);
1437                        $this->_refcursor = false;
1438                }
1439                @OCIFreeStatement($this->_queryID);
1440                $this->_queryID = false;
1441               
1442        }
1443
1444        function MetaType($t,$len=-1)
1445        {
1446                if (is_object($t)) {
1447                        $fieldobj = $t;
1448                        $t = $fieldobj->type;
1449                        $len = $fieldobj->max_length;
1450                }
1451                switch (strtoupper($t)) {
1452                case 'VARCHAR':
1453                case 'VARCHAR2':
1454                case 'CHAR':
1455                case 'VARBINARY':
1456                case 'BINARY':
1457                case 'NCHAR':
1458                case 'NVARCHAR':
1459                case 'NVARCHAR2':
1460                                 if (isset($this) && $len <= $this->blobSize) return 'C';
1461               
1462                case 'NCLOB':
1463                case 'LONG':
1464                case 'LONG VARCHAR':
1465                case 'CLOB':
1466                return 'X';
1467               
1468                case 'LONG RAW':
1469                case 'LONG VARBINARY':
1470                case 'BLOB':
1471                        return 'B';
1472               
1473                case 'DATE':
1474                        return  ($this->connection->datetime) ? 'T' : 'D';
1475               
1476               
1477                case 'TIMESTAMP': return 'T';
1478               
1479                case 'INT':
1480                case 'SMALLINT':
1481                case 'INTEGER':
1482                        return 'I';
1483                       
1484                default: return 'N';
1485                }
1486        }
1487}
1488
1489class ADORecordSet_ext_oci8 extends ADORecordSet_oci8 {
1490        function ADORecordSet_ext_oci8($queryID,$mode=false)
1491        {
1492                if ($mode === false) {
1493                        global $ADODB_FETCH_MODE;
1494                        $mode = $ADODB_FETCH_MODE;
1495                }
1496                switch ($mode)
1497                {
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;
1503                }
1504                $this->adodbFetchMode = $mode;
1505                $this->_queryID = $queryID;
1506        }
1507       
1508        function MoveNext()
1509        {
1510                return adodb_movenext($this);
1511        }
1512}
1513?>
Note: See TracBrowser for help on using the repository browser.