source: tmp/version-2_5-test/data/module/adodb/drivers/adodb-oci8.inc.php @ 18609

Revision 18609, 42.7 KB checked in by kajiwara, 14 years ago (diff)

正式版にナイトリービルド版をマージしてみるテスト

Line 
1<?php
2/*
3
4  version v4.992 10 Nov 2009 (c) 2000-2009 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                if (strncmp($argDatabasename,'SID=',4) == 0) {
203                    $argDatabasename = substr($argDatabasename,4);
204                    $this->connectSID = true;
205                }
206               
207                if ($this->connectSID) {
208                    $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
209                    .")(PORT=$argHostport))(CONNECT_DATA=(SID=$argDatabasename)))";
210                } else
211                    $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
212                    .")(PORT=$argHostport))(CONNECT_DATA=(SERVICE_NAME=$argDatabasename)))";
213            }
214        }
215               
216        //if ($argHostname) print "<p>Connect: 1st argument should be left blank for $this->databaseType</p>";
217        if ($mode==1) {
218            $this->_connectionID = ($this->charSet) ?
219                OCIPLogon($argUsername,$argPassword, $argDatabasename,$this->charSet)
220                :
221                OCIPLogon($argUsername,$argPassword, $argDatabasename)
222                ;
223            if ($this->_connectionID && $this->autoRollback)  OCIrollback($this->_connectionID);
224        } else if ($mode==2) {
225            $this->_connectionID = ($this->charSet) ?
226                OCINLogon($argUsername,$argPassword, $argDatabasename,$this->charSet)
227                :
228                OCINLogon($argUsername,$argPassword, $argDatabasename);
229               
230        } else {
231            $this->_connectionID = ($this->charSet) ?
232                OCILogon($argUsername,$argPassword, $argDatabasename,$this->charSet)
233                :
234                OCILogon($argUsername,$argPassword, $argDatabasename);
235        }
236        if (!$this->_connectionID) return false;
237        if ($this->_initdate) {
238            $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='".$this->NLS_DATE_FORMAT."'");
239        }
240       
241        // looks like:
242        // Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production With the Partitioning option JServer Release 8.1.7.0.0 - Production
243        // $vers = OCIServerVersion($this->_connectionID);
244        // if (strpos($vers,'8i') !== false) $this->ansiOuter = true;
245        return true;
246    }
247   
248    function ServerInfo()
249    {
250        $arr['compat'] = $this->GetOne('select value from sys.database_compatible_level');
251        $arr['description'] = @OCIServerVersion($this->_connectionID);
252        $arr['version'] = ADOConnection::_findvers($arr['description']);
253        return $arr;
254    }
255        // returns true or false
256    function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
257    {
258        return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,1);
259    }
260   
261    // returns true or false
262    function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
263    {
264        return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,2);
265    }
266   
267    function _affectedrows()
268    {
269        if (is_resource($this->_stmt)) return @OCIRowCount($this->_stmt);
270        return 0;
271    }
272   
273    function IfNull( $field, $ifNull )
274    {
275        return " NVL($field, $ifNull) "; // if Oracle
276    }
277   
278    // format and return date string in database date format
279    function DBDate($d)
280    {
281        if (empty($d) && $d !== 0) return 'null';
282       
283        if (is_string($d)) $d = ADORecordSet::UnixDate($d);
284        return "TO_DATE(".adodb_date($this->fmtDate,$d).",'".$this->dateformat."')";
285    }
286
287    function BindDate($d)
288    {
289        $d = ADOConnection::DBDate($d);
290        if (strncmp($d,"'",1)) return $d;
291       
292        return substr($d,1,strlen($d)-2);
293    }
294   
295    function BindTimeStamp($d)
296    {
297        $d = ADOConnection::DBTimeStamp($d);
298        if (strncmp($d,"'",1)) return $d;
299       
300        return substr($d,1,strlen($d)-2);
301    }
302   
303    // format and return date string in database timestamp format
304    function DBTimeStamp($ts)
305    {
306        if (empty($ts) && $ts !== 0) return 'null';
307        if (is_string($ts)) $ts = ADORecordSet::UnixTimeStamp($ts);
308        return 'TO_DATE('.adodb_date("'Y-m-d H:i:s'",$ts).",'RRRR-MM-DD, HH24:MI:SS')";
309    }
310   
311    function RowLock($tables,$where,$flds='1 as ignore')
312    {
313        if ($this->autoCommit) $this->BeginTrans();
314        return $this->GetOne("select $flds from $tables where $where for update");
315    }
316   
317    function &MetaTables($ttype=false,$showSchema=false,$mask=false)
318    {
319        if ($mask) {
320            $save = $this->metaTablesSQL;
321            $mask = $this->qstr(strtoupper($mask));
322            $this->metaTablesSQL .= " AND upper(table_name) like $mask";
323        }
324        $ret =& ADOConnection::MetaTables($ttype,$showSchema);
325       
326        if ($mask) {
327            $this->metaTablesSQL = $save;
328        }
329        return $ret;
330    }
331   
332    // Mark Newnham
333    function &MetaIndexes ($table, $primary = FALSE, $owner=false)
334    {
335        // save old fetch mode
336        global $ADODB_FETCH_MODE;
337
338        $save = $ADODB_FETCH_MODE;
339        $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
340
341        if ($this->fetchMode !== FALSE) {
342               $savem = $this->SetFetchMode(FALSE);
343        }
344
345        // get index details
346        $table = strtoupper($table);
347
348        // get Primary index
349        $primary_key = '';
350
351        $false = false;
352        $rs = $this->Execute(sprintf("SELECT * FROM ALL_CONSTRAINTS WHERE UPPER(TABLE_NAME)='%s' AND CONSTRAINT_TYPE='P'",$table));
353        if ($row = $rs->FetchRow())
354           $primary_key = $row[1]; //constraint_name
355
356        if ($primary==TRUE && $primary_key=='') {
357             if (isset($savem))
358                $this->SetFetchMode($savem);
359            $ADODB_FETCH_MODE = $save;
360            return $false; //There is no primary key
361        }
362
363        $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));
364
365       
366        if (!is_object($rs)) {
367            if (isset($savem))
368                $this->SetFetchMode($savem);
369            $ADODB_FETCH_MODE = $save;
370            return $false;
371        }
372
373        $indexes = array ();
374        // parse index data into array
375
376        while ($row = $rs->FetchRow()) {
377            if ($primary && $row[0] != $primary_key) continue;
378            if (!isset($indexes[$row[0]])) {
379                $indexes[$row[0]] = array(
380                   'unique' => ($row[1] == 'UNIQUE'),
381                   'columns' => array()
382                );
383            }
384            $indexes[$row[0]]['columns'][$row[2] - 1] = $row[3];
385        }
386
387        // sort columns by order in the index
388        foreach ( array_keys ($indexes) as $index ) {
389            ksort ($indexes[$index]['columns']);
390        }
391
392        if (isset($savem)) {
393            $this->SetFetchMode($savem);
394            $ADODB_FETCH_MODE = $save;
395        }
396        return $indexes;
397    }
398   
399    function BeginTrans()
400    {   
401        if ($this->transOff) return true;
402        $this->transCnt += 1;
403        $this->autoCommit = false;
404        $this->_commit = OCI_DEFAULT;
405       
406        if ($this->_transmode) $this->Execute("SET TRANSACTION ".$this->_transmode);
407        return true;
408    }
409   
410    function CommitTrans($ok=true)
411    {
412        if ($this->transOff) return true;
413        if (!$ok) return $this->RollbackTrans();
414       
415        if ($this->transCnt) $this->transCnt -= 1;
416        $ret = OCIcommit($this->_connectionID);
417        $this->_commit = OCI_COMMIT_ON_SUCCESS;
418        $this->autoCommit = true;
419        return $ret;
420    }
421   
422    function RollbackTrans()
423    {
424        if ($this->transOff) return true;
425        if ($this->transCnt) $this->transCnt -= 1;
426        $ret = OCIrollback($this->_connectionID);
427        $this->_commit = OCI_COMMIT_ON_SUCCESS;
428        $this->autoCommit = true;
429        return $ret;
430    }
431   
432   
433    function SelectDB($dbName)
434    {
435        return false;
436    }
437
438    function ErrorMsg()
439    {
440        if ($this->_errorMsg !== false) return $this->_errorMsg;
441
442        if (is_resource($this->_stmt)) $arr = @OCIError($this->_stmt);
443        if (empty($arr)) {
444            if (is_resource($this->_connectionID)) $arr = @OCIError($this->_connectionID);
445            else $arr = @OCIError();
446            if ($arr === false) return '';
447        }
448        $this->_errorMsg = $arr['message'];
449        $this->_errorCode = $arr['code'];
450        return $this->_errorMsg;
451    }
452
453    function ErrorNo()
454    {
455        if ($this->_errorCode !== false) return $this->_errorCode;
456       
457        if (is_resource($this->_stmt)) $arr = @OCIError($this->_stmt);
458        if (empty($arr)) {
459            $arr = @OCIError($this->_connectionID);
460            if ($arr == false) $arr = @OCIError();
461            if ($arr == false) return '';
462        }
463       
464        $this->_errorMsg = $arr['message'];
465        $this->_errorCode = $arr['code'];
466       
467        return $arr['code'];
468    }
469   
470    // Format date column in sql string given an input format that understands Y M D
471    function SQLDate($fmt, $col=false)
472    {   
473        if (!$col) $col = $this->sysTimeStamp;
474        $s = 'TO_CHAR('.$col.",'";
475       
476        $len = strlen($fmt);
477        for ($i=0; $i < $len; $i++) {
478            $ch = $fmt[$i];
479            switch($ch) {
480            case 'Y':
481            case 'y':
482                $s .= 'YYYY';
483                break;
484            case 'Q':
485            case 'q':
486                $s .= 'Q';
487                break;
488               
489            case 'M':
490                $s .= 'Mon';
491                break;
492               
493            case 'm':
494                $s .= 'MM';
495                break;
496            case 'D':
497            case 'd':
498                $s .= 'DD';
499                break;
500           
501            case 'H':
502                $s.= 'HH24';
503                break;
504               
505            case 'h':
506                $s .= 'HH';
507                break;
508               
509            case 'i':
510                $s .= 'MI';
511                break;
512           
513            case 's':
514                $s .= 'SS';
515                break;
516           
517            case 'a':
518            case 'A':
519                $s .= 'AM';
520                break;
521               
522            case 'w':
523                $s .= 'D';
524                break;
525               
526            case 'l':
527                $s .= 'DAY';
528                break;
529               
530             case 'W':
531                $s .= 'WW';
532                break;
533               
534            default:
535            // handle escape characters...
536                if ($ch == '\\') {
537                    $i++;
538                    $ch = substr($fmt,$i,1);
539                }
540                if (strpos('-/.:;, ',$ch) !== false) $s .= $ch;
541                else $s .= '"'.$ch.'"';
542               
543            }
544        }
545        return $s. "')";
546    }
547   
548    function GetRandRow($sql, $arr = false)
549    {
550        $sql = "SELECT * FROM ($sql ORDER BY dbms_random.value) WHERE rownum = 1";
551       
552        return $this->GetRow($sql,$arr);
553    }
554   
555    /*
556    This algorithm makes use of
557   
558    a. FIRST_ROWS hint
559    The FIRST_ROWS hint explicitly chooses the approach to optimize response time,
560    that is, minimum resource usage to return the first row. Results will be returned
561    as soon as they are identified.
562
563    b. Uses rownum tricks to obtain only the required rows from a given offset.
564     As this uses complicated sql statements, we only use this if the $offset >= 100.
565     This idea by Tomas V V Cox.
566     
567     This implementation does not appear to work with oracle 8.0.5 or earlier. Comment
568     out this function then, and the slower SelectLimit() in the base class will be used.
569    */
570    function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
571    {
572        // seems that oracle only supports 1 hint comment in 8i
573        if ($this->firstrows) {
574            if (strpos($sql,'/*+') !== false)
575                $sql = str_replace('/*+ ','/*+FIRST_ROWS ',$sql);
576            else
577                $sql = preg_replace('/^[ \t\n]*select/i','SELECT /*+FIRST_ROWS*/',$sql);
578        }
579       
580        if ($offset < $this->selectOffsetAlg1 && 0 < $nrows  && $nrows < 1000) {
581            if ($nrows > 0) {   
582                if ($offset > 0) $nrows += $offset;
583                //$inputarr['adodb_rownum'] = $nrows;
584                if ($this->databaseType == 'oci8po') {
585                    $sql = "select * from (".$sql.") where rownum <= ?";
586                } else {
587                    $sql = "select * from (".$sql.") where rownum <= :adodb_offset";
588                }
589                $inputarr['adodb_offset'] = $nrows;
590                $nrows = -1;
591            }
592            // note that $nrows = 0 still has to work ==> no rows returned
593
594            $rs =& ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
595            return $rs;
596           
597        } else {
598             // Algorithm by Tomas V V Cox, from PEAR DB oci8.php
599           
600             // Let Oracle return the name of the columns
601            $q_fields = "SELECT * FROM (".$sql.") WHERE NULL = NULL";
602             
603            $false = false;
604            if (! $stmt_arr = $this->Prepare($q_fields)) {
605                return $false;
606            }
607            $stmt = $stmt_arr[1];
608             
609             if (is_array($inputarr)) {
610                foreach($inputarr as $k => $v) {
611                    if (is_array($v)) {
612                        if (sizeof($v) == 2) // suggested by g.giunta@libero.
613                            OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]);
614                        else
615                            OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
616                    } else {
617                        $len = -1;
618                        if ($v === ' ') $len = 1;
619                        if (isset($bindarr)) {  // is prepared sql, so no need to ocibindbyname again
620                            $bindarr[$k] = $v;
621                        } else {                // dynamic sql, so rebind every time
622                            OCIBindByName($stmt,":$k",$inputarr[$k],$len);
623                        }
624                    }
625                }
626            }
627           
628             if (!OCIExecute($stmt, OCI_DEFAULT)) {
629                 OCIFreeStatement($stmt);
630                 return $false;
631             }
632             
633             $ncols = OCINumCols($stmt);
634             for ( $i = 1; $i <= $ncols; $i++ ) {
635                 $cols[] = '"'.OCIColumnName($stmt, $i).'"';
636             }
637             $result = false;
638           
639             OCIFreeStatement($stmt);
640             $fields = implode(',', $cols);
641             if ($nrows <= 0) $nrows = 999999999999;
642             else $nrows += $offset;
643             $offset += 1; // in Oracle rownum starts at 1
644           
645            if ($this->databaseType == 'oci8po') {
646                     $sql = "SELECT $fields FROM".
647                      "(SELECT rownum as adodb_rownum, $fields FROM".
648                      " ($sql) WHERE rownum <= ?".
649                      ") WHERE adodb_rownum >= ?";
650                } else {
651                     $sql = "SELECT $fields FROM".
652                      "(SELECT rownum as adodb_rownum, $fields FROM".
653                      " ($sql) WHERE rownum <= :adodb_nrows".
654                      ") WHERE adodb_rownum >= :adodb_offset";
655                }
656                $inputarr['adodb_nrows'] = $nrows;
657                $inputarr['adodb_offset'] = $offset;
658               
659            if ($secs2cache>0) $rs =& $this->CacheExecute($secs2cache, $sql,$inputarr);
660            else $rs =& $this->Execute($sql,$inputarr);
661            return $rs;
662        }
663   
664    }
665   
666    /**
667    * Usage:
668    * Store BLOBs and CLOBs
669    *
670    * Example: to store $var in a blob
671    *
672    *   $conn->Execute('insert into TABLE (id,ablob) values(12,empty_blob())');
673    *   $conn->UpdateBlob('TABLE', 'ablob', $varHoldingBlob, 'ID=12', 'BLOB');
674    *   
675    *   $blobtype supports 'BLOB' and 'CLOB', but you need to change to 'empty_clob()'.
676    *
677    *  to get length of LOB:
678    *   select DBMS_LOB.GETLENGTH(ablob) from TABLE
679    *
680    * If you are using CURSOR_SHARING = force, it appears this will case a segfault
681    * under oracle 8.1.7.0. Run:
682    *    $db->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT');
683    * before UpdateBlob() then...
684    */
685
686    function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
687    {
688       
689        //if (strlen($val) < 4000) return $this->Execute("UPDATE $table SET $column=:blob WHERE $where",array('blob'=>$val)) != false;
690       
691        switch(strtoupper($blobtype)) {
692        default: ADOConnection::outp("<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
693        case 'BLOB': $type = OCI_B_BLOB; break;
694        case 'CLOB': $type = OCI_B_CLOB; break;
695        }
696       
697        if ($this->databaseType == 'oci8po')
698            $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
699        else
700            $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
701       
702        $desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB);
703        $arr['blob'] = array($desc,-1,$type);
704        if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT');
705        $commit = $this->autoCommit;
706        if ($commit) $this->BeginTrans();
707        $rs = $this->_Execute($sql,$arr);
708        if ($rez = !empty($rs)) $desc->save($val);
709        $desc->free();
710        if ($commit) $this->CommitTrans();
711        if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=FORCE');
712       
713        if ($rez) $rs->Close();
714        return $rez;
715    }
716   
717    /**
718    * Usage:  store file pointed to by $var in a blob
719    */
720    function UpdateBlobFile($table,$column,$val,$where,$blobtype='BLOB')
721    {
722        switch(strtoupper($blobtype)) {
723        default: ADOConnection::outp( "<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
724        case 'BLOB': $type = OCI_B_BLOB; break;
725        case 'CLOB': $type = OCI_B_CLOB; break;
726        }
727       
728        if ($this->databaseType == 'oci8po')
729            $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
730        else
731            $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
732       
733        $desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB);
734        $arr['blob'] = array($desc,-1,$type);
735       
736        $this->BeginTrans();
737        $rs = ADODB_oci8::Execute($sql,$arr);
738        if ($rez = !empty($rs)) $desc->savefile($val);
739        $desc->free();
740        $this->CommitTrans();
741       
742        if ($rez) $rs->Close();
743        return $rez;
744    }
745
746        /**
747     * Execute SQL
748     *
749     * @param sql       SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text)
750     * @param [inputarr]    holds the input data to bind to. Null elements will be set to null.
751     * @return      RecordSet or false
752     */
753    function &Execute($sql,$inputarr=false)
754    {
755        if ($this->fnExecute) {
756            $fn = $this->fnExecute;
757            $ret =& $fn($this,$sql,$inputarr);
758            if (isset($ret)) return $ret;
759        }
760        if ($inputarr) {
761            #if (!is_array($inputarr)) $inputarr = array($inputarr);
762            $element0 = reset($inputarr);
763           
764            # is_object check because oci8 descriptors can be passed in
765            if (is_array($element0) && !is_object(reset($element0))) {
766                if (is_string($sql))
767                    $stmt = $this->Prepare($sql);
768                else
769                    $stmt = $sql;
770                   
771                foreach($inputarr as $arr) {
772                    $ret =& $this->_Execute($stmt,$arr);
773                    if (!$ret) return $ret;
774                }
775            } else {
776                $ret =& $this->_Execute($sql,$inputarr);
777            }
778           
779        } else {
780            $ret =& $this->_Execute($sql,false);
781        }
782
783        return $ret;
784    }
785   
786    /*
787        Example of usage:
788       
789        $stmt = $this->Prepare('insert into emp (empno, ename) values (:empno, :ename)');
790    */
791    function Prepare($sql,$cursor=false)
792    {
793    static $BINDNUM = 0;
794   
795        $stmt = OCIParse($this->_connectionID,$sql);
796
797        if (!$stmt) {
798            $this->_errorMsg = false;
799            $this->_errorCode = false;
800            $arr = @OCIError($this->_connectionID);
801            if ($arr === false) return false;
802       
803            $this->_errorMsg = $arr['message'];
804            $this->_errorCode = $arr['code'];
805            return false;
806        }
807       
808        $BINDNUM += 1;
809       
810        $sttype = @OCIStatementType($stmt);
811        if ($sttype == 'BEGIN' || $sttype == 'DECLARE') {
812            return array($sql,$stmt,0,$BINDNUM, ($cursor) ? OCINewCursor($this->_connectionID) : false);
813        }
814        return array($sql,$stmt,0,$BINDNUM);
815    }
816   
817    /*
818        Call an oracle stored procedure and returns a cursor variable as a recordset.
819        Concept by Robert Tuttle robert@ud.com
820       
821        Example:
822            Note: we return a cursor variable in :RS2
823            $rs = $db->ExecuteCursor("BEGIN adodb.open_tab(:RS2); END;",'RS2');
824           
825            $rs = $db->ExecuteCursor(
826                "BEGIN :RS2 = adodb.getdata(:VAR1); END;",
827                'RS2',
828                array('VAR1' => 'Mr Bean'));
829           
830    */
831    function &ExecuteCursor($sql,$cursorName='rs',$params=false)
832    {
833        if (is_array($sql)) $stmt = $sql;
834        else $stmt = ADODB_oci8::Prepare($sql,true); # true to allocate OCINewCursor
835   
836        if (is_array($stmt) && sizeof($stmt) >= 5) {
837            $hasref = true;
838            $ignoreCur = false;
839            $this->Parameter($stmt, $ignoreCur, $cursorName, false, -1, OCI_B_CURSOR);
840            if ($params) {
841                foreach($params as $k => $v) {
842                    $this->Parameter($stmt,$params[$k], $k);
843                }
844            }
845        } else
846            $hasref = false;
847           
848        $rs =& $this->Execute($stmt);
849        if ($rs) {
850            if ($rs->databaseType == 'array') OCIFreeCursor($stmt[4]);
851            else if ($hasref) $rs->_refcursor = $stmt[4];
852        }
853        return $rs;
854    }
855   
856    /*
857        Bind a variable -- very, very fast for executing repeated statements in oracle.
858        Better than using
859            for ($i = 0; $i < $max; $i++) {
860                $p1 = ?; $p2 = ?; $p3 = ?;
861                $this->Execute("insert into table (col0, col1, col2) values (:0, :1, :2)",
862                    array($p1,$p2,$p3));
863            }
864       
865        Usage:
866            $stmt = $DB->Prepare("insert into table (col0, col1, col2) values (:0, :1, :2)");
867            $DB->Bind($stmt, $p1);
868            $DB->Bind($stmt, $p2);
869            $DB->Bind($stmt, $p3);
870            for ($i = 0; $i < $max; $i++) {
871                $p1 = ?; $p2 = ?; $p3 = ?;
872                $DB->Execute($stmt);
873            }
874           
875        Some timings:       
876            ** Test table has 3 cols, and 1 index. Test to insert 1000 records
877            Time 0.6081s (1644.60 inserts/sec) with direct OCIParse/OCIExecute
878            Time 0.6341s (1577.16 inserts/sec) with ADOdb Prepare/Bind/Execute
879            Time 1.5533s ( 643.77 inserts/sec) with pure SQL using Execute
880           
881        Now if PHP only had batch/bulk updating like Java or PL/SQL...
882   
883        Note that the order of parameters differs from OCIBindByName,
884        because we default the names to :0, :1, :2
885    */
886    function Bind(&$stmt,&$var,$size=4000,$type=false,$name=false,$isOutput=false)
887    {
888       
889        if (!is_array($stmt)) return false;
890       
891        if (($type == OCI_B_CURSOR) && sizeof($stmt) >= 5) {
892            return OCIBindByName($stmt[1],":".$name,$stmt[4],$size,$type);
893        }
894       
895        if ($name == false) {
896            if ($type !== false) $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size,$type);
897            else $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size); // +1 byte for null terminator
898            $stmt[2] += 1;
899        } else if (oci_lob_desc($type)) {
900            if ($this->debug) {
901                ADOConnection::outp("<b>Bind</b>: name = $name");
902            }
903            //we have to create a new Descriptor here
904            $numlob = count($this->_refLOBs);
905            $this->_refLOBs[$numlob]['LOB'] = OCINewDescriptor($this->_connectionID, oci_lob_desc($type));
906            $this->_refLOBs[$numlob]['TYPE'] = $isOutput;
907           
908            $tmp = &$this->_refLOBs[$numlob]['LOB'];
909            $rez = OCIBindByName($stmt[1], ":".$name, $tmp, -1, $type);
910            if ($this->debug) {
911                ADOConnection::outp("<b>Bind</b>: descriptor has been allocated, var (".$name.") binded");
912            }
913           
914            // if type is input then write data to lob now
915            if ($isOutput == false) {
916                $var = $this->BlobEncode($var);
917                $tmp->WriteTemporary($var);
918                $this->_refLOBs[$numlob]['VAR'] = &$var;
919                if ($this->debug) {
920                    ADOConnection::outp("<b>Bind</b>: LOB has been written to temp");
921                }
922            } else {
923                $this->_refLOBs[$numlob]['VAR'] = $var;
924            }
925            $rez = $tmp;
926        } else {
927            if ($this->debug)
928                ADOConnection::outp("<b>Bind</b>: name = $name");
929           
930            if ($type !== false) $rez = OCIBindByName($stmt[1],":".$name,$var,$size,$type);
931            else $rez = OCIBindByName($stmt[1],":".$name,$var,$size); // +1 byte for null terminator
932        }
933       
934        return $rez;
935    }
936   
937    function Param($name,$type=false)
938    {
939        return ':'.$name;
940    }
941   
942    /*
943    Usage:
944        $stmt = $db->Prepare('select * from table where id =:myid and group=:group');
945        $db->Parameter($stmt,$id,'myid');
946        $db->Parameter($stmt,$group,'group');
947        $db->Execute($stmt);
948       
949        @param $stmt Statement returned by Prepare() or PrepareSP().
950        @param $var PHP variable to bind to
951        @param $name Name of stored procedure variable name to bind to.
952        @param [$isOutput] Indicates direction of parameter 0/false=IN  1=OUT  2= IN/OUT. This is ignored in oci8.
953        @param [$maxLen] Holds an maximum length of the variable.
954        @param [$type] The data type of $var. Legal values depend on driver.
955       
956        See OCIBindByName documentation at php.net.
957    */
958    function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
959    {
960            if  ($this->debug) {
961                $prefix = ($isOutput) ? 'Out' : 'In';
962                $ztype = (empty($type)) ? 'false' : $type;
963                ADOConnection::outp( "{$prefix}Parameter(\$stmt, \$php_var='$var', \$name='$name', \$maxLen=$maxLen, \$type=$ztype);");
964            }
965            return $this->Bind($stmt,$var,$maxLen,$type,$name,$isOutput);
966    }
967   
968    /*
969    returns query ID if successful, otherwise false
970    this version supports:
971   
972       1. $db->execute('select * from table');
973       
974       2. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
975          $db->execute($prepared_statement, array(1,2,3));
976         
977       3. $db->execute('insert into table (a,b,c) values (:a,:b,:c)',array('a'=>1,'b'=>2,'c'=>3));
978       
979       4. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
980          $db->bind($stmt,1); $db->bind($stmt,2); $db->bind($stmt,3);
981          $db->execute($stmt);
982    */
983    function _query($sql,$inputarr=false)
984    {
985        if (is_array($sql)) { // is prepared sql
986            $stmt = $sql[1];
987           
988            // we try to bind to permanent array, so that OCIBindByName is persistent
989            // and carried out once only - note that max array element size is 4000 chars
990            if (is_array($inputarr)) {
991                $bindpos = $sql[3];
992                if (isset($this->_bind[$bindpos])) {
993                // all tied up already
994                    $bindarr = &$this->_bind[$bindpos];
995                } else {
996                // one statement to bind them all
997                    $bindarr = array();
998                    foreach($inputarr as $k => $v) {
999                        $bindarr[$k] = $v;
1000                        OCIBindByName($stmt,":$k",$bindarr[$k],is_string($v) && strlen($v)>4000 ? -1 : 4000);
1001                    }
1002                    $this->_bind[$bindpos] = &$bindarr;
1003                }
1004            }
1005        } else {
1006            $stmt=OCIParse($this->_connectionID,$sql);
1007        }
1008           
1009        $this->_stmt = $stmt;
1010        if (!$stmt) return false;
1011   
1012        if (defined('ADODB_PREFETCH_ROWS')) @OCISetPrefetch($stmt,ADODB_PREFETCH_ROWS);
1013           
1014        if (is_array($inputarr)) {
1015            foreach($inputarr as $k => $v) {
1016                if (is_array($v)) {
1017                    if (sizeof($v) == 2) // suggested by g.giunta@libero.
1018                        OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]);
1019                    else
1020                        OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
1021                   
1022                    #if ($this->debug==99) echo "name=:$k",' var='.$inputarr[$k][0],' len='.$v[1],' type='.$v[2],'<br>';
1023                } else {
1024                    $len = -1;
1025                    if ($v === ' ') $len = 1;
1026                    if (isset($bindarr)) {  // is prepared sql, so no need to ocibindbyname again
1027                        $bindarr[$k] = $v;
1028                    } else {                // dynamic sql, so rebind every time
1029                        OCIBindByName($stmt,":$k",$inputarr[$k],$len);
1030                    }
1031                }
1032            }
1033        }
1034       
1035        $this->_errorMsg = false;
1036        $this->_errorCode = false;
1037        if (OCIExecute($stmt,$this->_commit)) {
1038//OCIInternalDebug(1);         
1039            if (count($this -> _refLOBs) > 0) {
1040       
1041                foreach ($this -> _refLOBs as $key => $value) {
1042                    if ($this -> _refLOBs[$key]['TYPE'] == true) {
1043                        $tmp = $this -> _refLOBs[$key]['LOB'] -> load();
1044                        if ($this -> debug) {
1045                            ADOConnection::outp("<b>OUT LOB</b>: LOB has been loaded. <br>");
1046                        }
1047                        //$_GLOBALS[$this -> _refLOBs[$key]['VAR']] = $tmp;
1048                        $this -> _refLOBs[$key]['VAR'] = $tmp;
1049                    } else {
1050                        $this->_refLOBs[$key]['LOB']->save($this->_refLOBs[$key]['VAR']);
1051                        $this -> _refLOBs[$key]['LOB']->free();
1052                        unset($this -> _refLOBs[$key]);
1053                        if ($this->debug) {
1054                            ADOConnection::outp("<b>IN LOB</b>: LOB has been saved. <br>");
1055                        }
1056                    }                   
1057                }
1058            }
1059       
1060            switch (@OCIStatementType($stmt)) {
1061                case "SELECT":
1062                    return $stmt;
1063               
1064                case 'DECLARE':
1065                case "BEGIN":
1066                    if (is_array($sql) && !empty($sql[4])) {
1067                        $cursor = $sql[4];
1068                        if (is_resource($cursor)) {
1069                            $ok = OCIExecute($cursor); 
1070                            return $cursor;
1071                        }
1072                        return $stmt;
1073                    } else {
1074                        if (is_resource($stmt)) {
1075                            OCIFreeStatement($stmt);
1076                            return true;
1077                        }
1078                        return $stmt;
1079                    }
1080                    break;
1081                default :
1082                    // ociclose -- no because it could be used in a LOB?
1083                    return true;
1084            }
1085        }
1086        return false;
1087    }
1088   
1089    // returns true or false
1090    function _close()
1091    {
1092        if (!$this->_connectionID) return;
1093       
1094        if (!$this->autoCommit) OCIRollback($this->_connectionID);
1095        if (count($this->_refLOBs) > 0) {
1096            foreach ($this ->_refLOBs as $key => $value) {
1097                $this->_refLOBs[$key]['LOB']->free();
1098                unset($this->_refLOBs[$key]);
1099            }
1100        }
1101        OCILogoff($this->_connectionID);
1102       
1103        $this->_stmt = false;
1104        $this->_connectionID = false;
1105    }
1106   
1107    function MetaPrimaryKeys($table, $owner=false,$internalKey=false)
1108    {
1109        if ($internalKey) return array('ROWID');
1110       
1111    // tested with oracle 8.1.7
1112        $table = strtoupper($table);
1113        if ($owner) {
1114            $owner_clause = "AND ((a.OWNER = b.OWNER) AND (a.OWNER = UPPER('$owner')))";
1115            $ptab = 'ALL_';
1116        } else {
1117            $owner_clause = '';
1118            $ptab = 'USER_';
1119        }
1120        $sql = "
1121SELECT /*+ RULE */ distinct b.column_name
1122   FROM {$ptab}CONSTRAINTS a
1123      , {$ptab}CONS_COLUMNS b
1124  WHERE ( UPPER(b.table_name) = ('$table'))
1125    AND (UPPER(a.table_name) = ('$table') and a.constraint_type = 'P')
1126    $owner_clause
1127    AND (a.constraint_name = b.constraint_name)";
1128
1129        $rs = $this->Execute($sql);
1130        if ($rs && !$rs->EOF) {
1131            $arr =& $rs->GetArray();
1132            $a = array();
1133            foreach($arr as $v) {
1134                $a[] = reset($v);
1135            }
1136            return $a;
1137        }
1138        else return false;
1139    }
1140   
1141    // http://gis.mit.edu/classes/11.521/sqlnotes/referential_integrity.html
1142    function MetaForeignKeys($table, $owner=false)
1143    {
1144    global $ADODB_FETCH_MODE;
1145   
1146        $save = $ADODB_FETCH_MODE;
1147        $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
1148        $table = $this->qstr(strtoupper($table));
1149        if (!$owner) {
1150            $owner = $this->user;
1151            $tabp = 'user_';
1152        } else
1153            $tabp = 'all_';
1154           
1155        $owner = ' and owner='.$this->qstr(strtoupper($owner));
1156       
1157        $sql =
1158"select constraint_name,r_owner,r_constraint_name
1159    from {$tabp}constraints
1160    where constraint_type = 'R' and table_name = $table $owner";
1161       
1162        $constraints =& $this->GetArray($sql);
1163        $arr = false;
1164        foreach($constraints as $constr) {
1165            $cons = $this->qstr($constr[0]);
1166            $rowner = $this->qstr($constr[1]);
1167            $rcons = $this->qstr($constr[2]);
1168            $cols = $this->GetArray("select column_name from {$tabp}cons_columns where constraint_name=$cons $owner order by position");
1169            $tabcol = $this->GetArray("select table_name,column_name from {$tabp}cons_columns where owner=$rowner and constraint_name=$rcons order by position");
1170           
1171            if ($cols && $tabcol)
1172                for ($i=0, $max=sizeof($cols); $i < $max; $i++) {
1173                    $arr[$tabcol[$i][0]] = $cols[$i][0].'='.$tabcol[$i][1];
1174                }
1175        }
1176        $ADODB_FETCH_MODE = $save;
1177       
1178        return $arr;
1179    }
1180
1181   
1182    function CharMax()
1183    {
1184        return 4000;
1185    }
1186   
1187    function TextMax()
1188    {
1189        return 4000;
1190    }
1191   
1192    /**
1193     * Quotes a string.
1194     * An example is  $db->qstr("Don't bother",magic_quotes_runtime());
1195     *
1196     * @param s         the string to quote
1197     * @param [magic_quotes]    if $s is GET/POST var, set to get_magic_quotes_gpc().
1198     *              This undoes the stupidity of magic quotes for GPC.
1199     *
1200     * @return  quoted string to be sent back to database
1201     */
1202    function qstr($s,$magic_quotes=false)
1203    {   
1204        //$nofixquotes=false;
1205   
1206        if ($this->noNullStrings && strlen($s)==0)$s = ' ';
1207        if (!$magic_quotes) {   
1208            if ($this->replaceQuote[0] == '\\'){
1209                $s = str_replace('\\','\\\\',$s);
1210            }
1211            return  "'".str_replace("'",$this->replaceQuote,$s)."'";
1212        }
1213       
1214        // undo magic quotes for "
1215        $s = str_replace('\\"','"',$s);
1216       
1217        $s = str_replace('\\\\','\\',$s);
1218        return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
1219       
1220    }
1221   
1222}
1223
1224/*--------------------------------------------------------------------------------------
1225         Class Name: Recordset
1226--------------------------------------------------------------------------------------*/
1227
1228class ADORecordset_oci8 extends ADORecordSet {
1229
1230    var $databaseType = 'oci8';
1231    var $bind=false;
1232    var $_fieldobjs;
1233   
1234    //var $_arr = false;
1235       
1236    function ADORecordset_oci8($queryID,$mode=false)
1237    {
1238        if ($mode === false) {
1239            global $ADODB_FETCH_MODE;
1240            $mode = $ADODB_FETCH_MODE;
1241        }
1242        switch ($mode)
1243        {
1244        case ADODB_FETCH_ASSOC:$this->fetchMode = OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
1245        case ADODB_FETCH_DEFAULT:
1246        case ADODB_FETCH_BOTH:$this->fetchMode = OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
1247        case ADODB_FETCH_NUM:
1248        default:
1249        $this->fetchMode = OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
1250        }
1251       
1252        $this->adodbFetchMode = $mode;
1253        $this->_queryID = $queryID;
1254    }
1255
1256
1257    function Init()
1258    {
1259        if ($this->_inited) return;
1260       
1261        $this->_inited = true;
1262        if ($this->_queryID) {
1263           
1264            $this->_currentRow = 0;
1265            @$this->_initrs();
1266            $this->EOF = !$this->_fetch();
1267           
1268            /*
1269            // based on idea by Gaetano Giunta to detect unusual oracle errors
1270            // see http://phplens.com/lens/lensforum/msgs.php?id=6771
1271            $err = OCIError($this->_queryID);
1272            if ($err && $this->connection->debug) ADOConnection::outp($err);
1273            */
1274           
1275            if (!is_array($this->fields)) {
1276                $this->_numOfRows = 0;
1277                $this->fields = array();
1278            }
1279        } else {
1280            $this->fields = array();
1281            $this->_numOfRows = 0;
1282            $this->_numOfFields = 0;
1283            $this->EOF = true;
1284        }
1285    }
1286   
1287    function _initrs()
1288    {
1289        $this->_numOfRows = -1;
1290        $this->_numOfFields = OCInumcols($this->_queryID);
1291        if ($this->_numOfFields>0) {
1292            $this->_fieldobjs = array();
1293            $max = $this->_numOfFields;
1294            for ($i=0;$i<$max; $i++) $this->_fieldobjs[] = $this->_FetchField($i);
1295        }
1296    }
1297
1298      /*        Returns: an object containing field information.
1299              Get column information in the Recordset object. fetchField() can be used in order to obtain information about
1300              fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
1301              fetchField() is retrieved.        */
1302
1303    function _FetchField($fieldOffset = -1)
1304    {
1305        $fld = new ADOFieldObject;
1306        $fieldOffset += 1;
1307        $fld->name =OCIcolumnname($this->_queryID, $fieldOffset);
1308        $fld->type = OCIcolumntype($this->_queryID, $fieldOffset);
1309        $fld->max_length = OCIcolumnsize($this->_queryID, $fieldOffset);
1310        switch($fld->type) {
1311        case 'NUMBER':
1312            $p = OCIColumnPrecision($this->_queryID, $fieldOffset);
1313            $sc = OCIColumnScale($this->_queryID, $fieldOffset);
1314            if ($p != 0 && $sc == 0) $fld->type = 'INT';
1315            break;
1316       
1317        case 'CLOB':
1318        case 'NCLOB':
1319        case 'BLOB':
1320            $fld->max_length = -1;
1321            break;
1322        }
1323        return $fld;
1324    }
1325   
1326    /* For some reason, OCIcolumnname fails when called after _initrs() so we cache it */
1327    function &FetchField($fieldOffset = -1)
1328    {
1329        return $this->_fieldobjs[$fieldOffset];
1330    }
1331   
1332   
1333    /*
1334    // 10% speedup to move MoveNext to child class
1335    function _MoveNext()
1336    {
1337    //global $ADODB_EXTENSION;if ($ADODB_EXTENSION) return @adodb_movenext($this);
1338       
1339        if ($this->EOF) return false;
1340       
1341        $this->_currentRow++;
1342        if(@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode))
1343            return true;
1344        $this->EOF = true;
1345       
1346        return false;
1347    }   */
1348   
1349   
1350    function MoveNext()
1351    {
1352        if (@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) {
1353            $this->_currentRow += 1;
1354            return true;
1355        }
1356        if (!$this->EOF) {
1357            $this->_currentRow += 1;
1358            $this->EOF = true;
1359        }
1360        return false;
1361    }
1362   
1363    /*
1364    # does not work as first record is retrieved in _initrs(), so is not included in GetArray()
1365    function &GetArray($nRows = -1)
1366    {
1367    global $ADODB_OCI8_GETARRAY;
1368   
1369        if (true ||  !empty($ADODB_OCI8_GETARRAY)) {
1370            # does not support $ADODB_ANSI_PADDING_OFF
1371   
1372            //OCI_RETURN_NULLS and OCI_RETURN_LOBS is set by OCIfetchstatement
1373            switch($this->adodbFetchMode) {
1374            case ADODB_FETCH_NUM:
1375           
1376                $ncols = @OCIfetchstatement($this->_queryID, $results, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW+OCI_NUM);
1377                $results = array_merge(array($this->fields),$results);
1378                return $results;
1379               
1380            case ADODB_FETCH_ASSOC:
1381                if (ADODB_ASSOC_CASE != 2 || $this->databaseType != 'oci8') break;
1382               
1383                $ncols = @OCIfetchstatement($this->_queryID, $assoc, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW);
1384                $results =& array_merge(array($this->fields),$assoc);
1385                return $results;
1386           
1387            default:
1388                break;
1389            }
1390        }
1391           
1392        $results =& ADORecordSet::GetArray($nRows);
1393        return $results;
1394       
1395    } */
1396   
1397    /* Optimize SelectLimit() by using OCIFetch() instead of OCIFetchInto() */
1398    function &GetArrayLimit($nrows,$offset=-1)
1399    {
1400        if ($offset <= 0) {
1401            $arr =& $this->GetArray($nrows);
1402            return $arr;
1403        }
1404        $arr = array();
1405        for ($i=1; $i < $offset; $i++)
1406            if (!@OCIFetch($this->_queryID)) return $arr;
1407           
1408        if (!@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) return $arr;;
1409        $results = array();
1410        $cnt = 0;
1411        while (!$this->EOF && $nrows != $cnt) {
1412            $results[$cnt++] = $this->fields;
1413            $this->MoveNext();
1414        }
1415       
1416        return $results;
1417    }
1418
1419   
1420    /* Use associative array to get fields array */
1421    function Fields($colname)
1422    {
1423        if (!$this->bind) {
1424            $this->bind = array();
1425            for ($i=0; $i < $this->_numOfFields; $i++) {
1426                $o = $this->FetchField($i);
1427                $this->bind[strtoupper($o->name)] = $i;
1428            }
1429        }
1430       
1431         return $this->fields[$this->bind[strtoupper($colname)]];
1432    }
1433   
1434
1435
1436    function _seek($row)
1437    {
1438        return false;
1439    }
1440
1441    function _fetch()
1442    {
1443        return @OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode);
1444    }
1445
1446    /*      close() only needs to be called if you are worried about using too much memory while your script
1447            is running. All associated result memory for the specified result identifier will automatically be freed.       */
1448
1449    function _close()
1450    {
1451        if ($this->connection->_stmt === $this->_queryID) $this->connection->_stmt = false;
1452        if (!empty($this->_refcursor)) {
1453            OCIFreeCursor($this->_refcursor);
1454            $this->_refcursor = false;
1455        }
1456        @OCIFreeStatement($this->_queryID);
1457        $this->_queryID = false;
1458       
1459    }
1460
1461    function MetaType($t,$len=-1)
1462    {
1463        if (is_object($t)) {
1464            $fieldobj = $t;
1465            $t = $fieldobj->type;
1466            $len = $fieldobj->max_length;
1467        }
1468        switch (strtoupper($t)) {
1469        case 'VARCHAR':
1470        case 'VARCHAR2':
1471        case 'CHAR':
1472        case 'VARBINARY':
1473        case 'BINARY':
1474        case 'NCHAR':
1475        case 'NVARCHAR':
1476        case 'NVARCHAR2':
1477                 if (isset($this) && $len <= $this->blobSize) return 'C';
1478       
1479        case 'NCLOB':
1480        case 'LONG':
1481        case 'LONG VARCHAR':
1482        case 'CLOB':
1483        return 'X';
1484       
1485        case 'LONG RAW':
1486        case 'LONG VARBINARY':
1487        case 'BLOB':
1488            return 'B';
1489       
1490        case 'DATE':
1491            return  ($this->connection->datetime) ? 'T' : 'D';
1492       
1493       
1494        case 'TIMESTAMP': return 'T';
1495       
1496        case 'INT':
1497        case 'SMALLINT':
1498        case 'INTEGER':
1499            return 'I';
1500           
1501        default: return 'N';
1502        }
1503    }
1504}
1505
1506class ADORecordSet_ext_oci8 extends ADORecordSet_oci8 {
1507    function ADORecordSet_ext_oci8($queryID,$mode=false)
1508    {
1509        if ($mode === false) {
1510            global $ADODB_FETCH_MODE;
1511            $mode = $ADODB_FETCH_MODE;
1512        }
1513        switch ($mode)
1514        {
1515        case ADODB_FETCH_ASSOC:$this->fetchMode = OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
1516        case ADODB_FETCH_DEFAULT:
1517        case ADODB_FETCH_BOTH:$this->fetchMode = OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
1518        case ADODB_FETCH_NUM:
1519        default: $this->fetchMode = OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
1520        }
1521        $this->adodbFetchMode = $mode;
1522        $this->_queryID = $queryID;
1523    }
1524   
1525    function MoveNext()
1526    {
1527        return adodb_movenext($this);
1528    }
1529}
1530?>
Note: See TracBrowser for help on using the repository browser.