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