Subversion Repositories svnkaklik

Rev

Details | Last modification | View Log

Rev Author Line No. Line
36 kaklik 1
<?php
2
/*
3
 
4
  version V4.80 8 Mar 2006 (c) 2000-2006 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
18
if (!defined('ADODB_DIR')) die();
19
 
20
/*
21
NLS_Date_Format
22
Allows you to use a date format other than the Oracle Lite default. When a literal
23
character string appears where a date value is expected, the Oracle Lite database
24
tests the string to see if it matches the formats of Oracle, SQL-92, or the value
25
specified for this parameter in the POLITE.INI file. Setting this parameter also
26
defines the default format used in the TO_CHAR or TO_DATE functions when no
27
other format string is supplied.
28
 
29
For Oracle the default is dd-mon-yy or dd-mon-yyyy, and for SQL-92 the default is
30
yy-mm-dd or yyyy-mm-dd.
31
 
32
Using 'RR' in the format forces two-digit years less than or equal to 49 to be
33
interpreted as years in the 21st century (2000–2049), and years over 50 as years in
34
the 20th century (1950–1999). Setting the RR format as the default for all two-digit
35
year entries allows you to become year-2000 compliant. For example:
36
NLS_DATE_FORMAT='RR-MM-DD'
37
 
38
You 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
43
function 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
 
55
class 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
 
167
Example TNSName:
168
---------------
169
NATSOFT.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 = empty($this->port)?  "1521" : $this->port;
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 upper(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
			 case 'W':
508
				$s .= 'WW';
509
				break;
510
 
511
			default:
512
			// handle escape characters...
513
				if ($ch == '\\') {
514
					$i++;
515
					$ch = substr($fmt,$i,1);
516
				}
517
				if (strpos('-/.:;, ',$ch) !== false) $s .= $ch;
518
				else $s .= '"'.$ch.'"';
519
 
520
			}
521
		}
522
		return $s. "')";
523
	}
524
 
525
 
526
	/*
527
	This algorithm makes use of
528
 
529
	a. FIRST_ROWS hint
530
	The FIRST_ROWS hint explicitly chooses the approach to optimize response time, 
531
	that is, minimum resource usage to return the first row. Results will be returned 
532
	as soon as they are identified. 
533
 
534
	b. Uses rownum tricks to obtain only the required rows from a given offset.
535
	 As this uses complicated sql statements, we only use this if the $offset >= 100. 
536
	 This idea by Tomas V V Cox.
537
 
538
	 This implementation does not appear to work with oracle 8.0.5 or earlier. Comment
539
	 out this function then, and the slower SelectLimit() in the base class will be used.
540
	*/
541
	function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
542
	{
543
		// seems that oracle only supports 1 hint comment in 8i
544
		if ($this->firstrows) {
545
			if (strpos($sql,'/*+') !== false)
546
				$sql = str_replace('/*+ ','/*+FIRST_ROWS ',$sql);
547
			else
548
				$sql = preg_replace('/^[ \t\n]*select/i','SELECT /*+FIRST_ROWS*/',$sql);
549
		}
550
 
551
		if ($offset < $this->selectOffsetAlg1) {
552
			if ($nrows > 0) {	
553
				if ($offset > 0) $nrows += $offset;
554
				//$inputarr['adodb_rownum'] = $nrows;
555
				if ($this->databaseType == 'oci8po') {
556
					$sql = "select * from (".$sql.") where rownum <= ?";
557
				} else {
558
					$sql = "select * from (".$sql.") where rownum <= :adodb_offset";
559
				} 
560
				$inputarr['adodb_offset'] = $nrows;
561
				$nrows = -1;
562
			}
563
			// note that $nrows = 0 still has to work ==> no rows returned
564
 
565
			$rs =& ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
566
			return $rs;
567
 
568
		} else {
569
			 // Algorithm by Tomas V V Cox, from PEAR DB oci8.php
570
 
571
			 // Let Oracle return the name of the columns
572
			$q_fields = "SELECT * FROM (".$sql.") WHERE NULL = NULL";
573
 
574
			$false = false;
575
			if (! $stmt_arr = $this->Prepare($q_fields)) {
576
				return $false;
577
			}
578
			$stmt = $stmt_arr[1];
579
 
580
			 if (is_array($inputarr)) {
581
			 	foreach($inputarr as $k => $v) {
582
					if (is_array($v)) {
583
						if (sizeof($v) == 2) // suggested by g.giunta@libero.
584
							OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]);
585
						else
586
							OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
587
					} else {
588
						$len = -1;
589
						if ($v === ' ') $len = 1;
590
						if (isset($bindarr)) {	// is prepared sql, so no need to ocibindbyname again
591
							$bindarr[$k] = $v;
592
						} else { 				// dynamic sql, so rebind every time
593
							OCIBindByName($stmt,":$k",$inputarr[$k],$len);
594
						}
595
					}
596
				}
597
			}
598
 
599
			 if (!OCIExecute($stmt, OCI_DEFAULT)) {
600
				 OCIFreeStatement($stmt); 
601
				 return $false;
602
			 }
603
 
604
			 $ncols = OCINumCols($stmt);
605
			 for ( $i = 1; $i <= $ncols; $i++ ) {
606
				 $cols[] = '"'.OCIColumnName($stmt, $i).'"';
607
			 }
608
			 $result = false;
609
 
610
			 OCIFreeStatement($stmt); 
611
			 $fields = implode(',', $cols);
612
			 $nrows += $offset;
613
			 $offset += 1; // in Oracle rownum starts at 1
614
 
615
			if ($this->databaseType == 'oci8po') {
616
					 $sql = "SELECT $fields FROM".
617
					  "(SELECT rownum as adodb_rownum, $fields FROM".
618
					  " ($sql) WHERE rownum <= ?".
619
					  ") WHERE adodb_rownum >= ?";
620
				} else {
621
					 $sql = "SELECT $fields FROM".
622
					  "(SELECT rownum as adodb_rownum, $fields FROM".
623
					  " ($sql) WHERE rownum <= :adodb_nrows".
624
					  ") WHERE adodb_rownum >= :adodb_offset";
625
				} 
626
				$inputarr['adodb_nrows'] = $nrows;
627
				$inputarr['adodb_offset'] = $offset;
628
 
629
			if ($secs2cache>0) $rs =& $this->CacheExecute($secs2cache, $sql,$inputarr);
630
			else $rs =& $this->Execute($sql,$inputarr);
631
			return $rs;
632
		}
633
 
634
	}
635
 
636
	/**
637
	* Usage:
638
	* Store BLOBs and CLOBs
639
	*
640
	* Example: to store $var in a blob
641
	*
642
	*	$conn->Execute('insert into TABLE (id,ablob) values(12,empty_blob())');
643
	*	$conn->UpdateBlob('TABLE', 'ablob', $varHoldingBlob, 'ID=12', 'BLOB');
644
	*	
645
	*	$blobtype supports 'BLOB' and 'CLOB', but you need to change to 'empty_clob()'.
646
	*
647
	*  to get length of LOB:
648
	*  	select DBMS_LOB.GETLENGTH(ablob) from TABLE
649
	*
650
	* If you are using CURSOR_SHARING = force, it appears this will case a segfault
651
	* under oracle 8.1.7.0. Run:
652
	*	 $db->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT');
653
	* before UpdateBlob() then...
654
	*/
655
 
656
	function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
657
	{
658
 
659
		//if (strlen($val) < 4000) return $this->Execute("UPDATE $table SET $column=:blob WHERE $where",array('blob'=>$val)) != false;
660
 
661
		switch(strtoupper($blobtype)) {
662
		default: ADOConnection::outp("<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
663
		case 'BLOB': $type = OCI_B_BLOB; break;
664
		case 'CLOB': $type = OCI_B_CLOB; break;
665
		}
666
 
667
		if ($this->databaseType == 'oci8po') 
668
			$sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
669
		else 
670
			$sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
671
 
672
		$desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB);
673
		$arr['blob'] = array($desc,-1,$type);
674
		if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT');
675
		$commit = $this->autoCommit;
676
		if ($commit) $this->BeginTrans();
677
		$rs = $this->_Execute($sql,$arr);
678
		if ($rez = !empty($rs)) $desc->save($val);
679
		$desc->free();
680
		if ($commit) $this->CommitTrans();
681
		if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=FORCE');
682
 
683
		if ($rez) $rs->Close();
684
		return $rez;
685
	}
686
 
687
	/**
688
	* Usage:  store file pointed to by $var in a blob
689
	*/
690
	function UpdateBlobFile($table,$column,$val,$where,$blobtype='BLOB')
691
	{
692
		switch(strtoupper($blobtype)) {
693
		default: ADOConnection::outp( "<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
694
		case 'BLOB': $type = OCI_B_BLOB; break;
695
		case 'CLOB': $type = OCI_B_CLOB; break;
696
		}
697
 
698
		if ($this->databaseType == 'oci8po') 
699
			$sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
700
		else 
701
			$sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
702
 
703
		$desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB);
704
		$arr['blob'] = array($desc,-1,$type);
705
 
706
		$this->BeginTrans();
707
		$rs = ADODB_oci8::Execute($sql,$arr);
708
		if ($rez = !empty($rs)) $desc->savefile($val);
709
		$desc->free();
710
		$this->CommitTrans();
711
 
712
		if ($rez) $rs->Close();
713
		return $rez;
714
	}
715
 
716
		/**
717
	 * Execute SQL 
718
	 *
719
	 * @param sql		SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text)
720
	 * @param [inputarr]	holds the input data to bind to. Null elements will be set to null.
721
	 * @return 		RecordSet or false
722
	 */
723
	function &Execute($sql,$inputarr=false) 
724
	{
725
		if ($this->fnExecute) {
726
			$fn = $this->fnExecute;
727
			$ret =& $fn($this,$sql,$inputarr);
728
			if (isset($ret)) return $ret;
729
		}
730
		if ($inputarr) {
731
			#if (!is_array($inputarr)) $inputarr = array($inputarr);
732
 
733
			$element0 = reset($inputarr);
734
 
735
			# is_object check because oci8 descriptors can be passed in
736
			if (is_array($element0) && !is_object(reset($element0))) {
737
				if (is_string($sql))
738
					$stmt = $this->Prepare($sql);
739
				else
740
					$stmt = $sql;
741
 
742
				foreach($inputarr as $arr) {
743
					$ret =& $this->_Execute($stmt,$arr);
744
					if (!$ret) return $ret;
745
				}
746
			} else {
747
				$ret =& $this->_Execute($sql,$inputarr);
748
			}
749
 
750
		} else {
751
			$ret =& $this->_Execute($sql,false);
752
		}
753
 
754
		return $ret;
755
	}
756
 
757
	/*
758
		Example of usage:
759
 
760
		$stmt = $this->Prepare('insert into emp (empno, ename) values (:empno, :ename)');
761
	*/
762
	function Prepare($sql,$cursor=false)
763
	{
764
	static $BINDNUM = 0;
765
 
766
		$stmt = OCIParse($this->_connectionID,$sql);
767
 
768
		if (!$stmt) return false;
769
 
770
		$BINDNUM += 1;
771
 
772
		$sttype = @OCIStatementType($stmt);
773
		if ($sttype == 'BEGIN' || $sttype == 'DECLARE') {
774
			return array($sql,$stmt,0,$BINDNUM, ($cursor) ? OCINewCursor($this->_connectionID) : false);
775
		}
776
		return array($sql,$stmt,0,$BINDNUM);
777
	}
778
 
779
	/*
780
		Call an oracle stored procedure and returns a cursor variable as a recordset. 
781
		Concept by Robert Tuttle robert@ud.com
782
 
783
		Example:
784
			Note: we return a cursor variable in :RS2
785
			$rs = $db->ExecuteCursor("BEGIN adodb.open_tab(:RS2); END;",'RS2');
786
 
787
			$rs = $db->ExecuteCursor(
788
				"BEGIN :RS2 = adodb.getdata(:VAR1); END;", 
789
				'RS2',
790
				array('VAR1' => 'Mr Bean'));
791
 
792
	*/
793
	function &ExecuteCursor($sql,$cursorName='rs',$params=false)
794
	{
795
		if (is_array($sql)) $stmt = $sql;
796
		else $stmt = ADODB_oci8::Prepare($sql,true); # true to allocate OCINewCursor
797
 
798
		if (is_array($stmt) && sizeof($stmt) >= 5) {
799
			$hasref = true;
800
			$ignoreCur = false;
801
			$this->Parameter($stmt, $ignoreCur, $cursorName, false, -1, OCI_B_CURSOR);
802
			if ($params) {
803
				foreach($params as $k => $v) {
804
					$this->Parameter($stmt,$params[$k], $k);
805
				}
806
			}
807
		} else
808
			$hasref = false;
809
 
810
		$rs =& $this->Execute($stmt);
811
		if ($rs) {
812
			if ($rs->databaseType == 'array') OCIFreeCursor($stmt[4]);
813
			else if ($hasref) $rs->_refcursor = $stmt[4];
814
		}
815
		return $rs;
816
	}
817
 
818
	/*
819
		Bind a variable -- very, very fast for executing repeated statements in oracle. 
820
		Better than using
821
			for ($i = 0; $i < $max; $i++) {	
822
				$p1 = ?; $p2 = ?; $p3 = ?;
823
				$this->Execute("insert into table (col0, col1, col2) values (:0, :1, :2)", 
824
					array($p1,$p2,$p3));
825
			}
826
 
827
		Usage:
828
			$stmt = $DB->Prepare("insert into table (col0, col1, col2) values (:0, :1, :2)");
829
			$DB->Bind($stmt, $p1);
830
			$DB->Bind($stmt, $p2);
831
			$DB->Bind($stmt, $p3);
832
			for ($i = 0; $i < $max; $i++) {	
833
				$p1 = ?; $p2 = ?; $p3 = ?;
834
				$DB->Execute($stmt);
835
			}
836
 
837
		Some timings:		
838
			** Test table has 3 cols, and 1 index. Test to insert 1000 records
839
			Time 0.6081s (1644.60 inserts/sec) with direct OCIParse/OCIExecute
840
			Time 0.6341s (1577.16 inserts/sec) with ADOdb Prepare/Bind/Execute
841
			Time 1.5533s ( 643.77 inserts/sec) with pure SQL using Execute
842
 
843
		Now if PHP only had batch/bulk updating like Java or PL/SQL...
844
 
845
		Note that the order of parameters differs from OCIBindByName,
846
		because we default the names to :0, :1, :2
847
	*/
848
	function Bind(&$stmt,&$var,$size=4000,$type=false,$name=false,$isOutput=false)
849
	{
850
 
851
		if (!is_array($stmt)) return false;
852
 
853
        if (($type == OCI_B_CURSOR) && sizeof($stmt) >= 5) { 
854
            return OCIBindByName($stmt[1],":".$name,$stmt[4],$size,$type);
855
        }
856
 
857
		if ($name == false) {
858
			if ($type !== false) $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size,$type);
859
			else $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size); // +1 byte for null terminator
860
			$stmt[2] += 1;
861
		} else if (oci_lob_desc($type)) {
862
			if ($this->debug) {
863
				ADOConnection::outp("<b>Bind</b>: name = $name");
864
			}
865
            //we have to create a new Descriptor here
866
			$numlob = count($this->_refLOBs);
867
        	$this->_refLOBs[$numlob]['LOB'] = OCINewDescriptor($this->_connectionID, oci_lob_desc($type));
868
			$this->_refLOBs[$numlob]['TYPE'] = $isOutput;
869
 
870
			$tmp = &$this->_refLOBs[$numlob]['LOB'];
871
	        $rez = OCIBindByName($stmt[1], ":".$name, $tmp, -1, $type);
872
			if ($this->debug) {
873
				ADOConnection::outp("<b>Bind</b>: descriptor has been allocated, var (".$name.") binded");
874
			}
875
 
876
			// if type is input then write data to lob now
877
			if ($isOutput == false) {
878
				$var = $this->BlobEncode($var);
879
				$tmp->WriteTemporary($var);
880
				$this->_refLOBs[$numlob]['VAR'] = &$var;
881
				if ($this->debug) {
882
					ADOConnection::outp("<b>Bind</b>: LOB has been written to temp");
883
				}
884
			} else {
885
				$this->_refLOBs[$numlob]['VAR'] = &$var;
886
			}
887
			$rez = $tmp;
888
		} else {
889
			if ($this->debug) 
890
				ADOConnection::outp("<b>Bind</b>: name = $name");
891
 
892
			if ($type !== false) $rez = OCIBindByName($stmt[1],":".$name,$var,$size,$type);
893
			else $rez = OCIBindByName($stmt[1],":".$name,$var,$size); // +1 byte for null terminator
894
		}
895
 
896
		return $rez;
897
	}
898
 
899
	function Param($name,$type=false)
900
	{
901
		return ':'.$name;
902
	}
903
 
904
	/* 
905
	Usage:
906
		$stmt = $db->Prepare('select * from table where id =:myid and group=:group');
907
		$db->Parameter($stmt,$id,'myid');
908
		$db->Parameter($stmt,$group,'group');
909
		$db->Execute($stmt);
910
 
911
		@param $stmt Statement returned by Prepare() or PrepareSP().
912
		@param $var PHP variable to bind to
913
		@param $name Name of stored procedure variable name to bind to.
914
		@param [$isOutput] Indicates direction of parameter 0/false=IN  1=OUT  2= IN/OUT. This is ignored in oci8.
915
		@param [$maxLen] Holds an maximum length of the variable.
916
		@param [$type] The data type of $var. Legal values depend on driver.
917
 
918
		See OCIBindByName documentation at php.net.
919
	*/
920
	function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
921
	{
922
			if  ($this->debug) {
923
				$prefix = ($isOutput) ? 'Out' : 'In';
924
				$ztype = (empty($type)) ? 'false' : $type;
925
				ADOConnection::outp( "{$prefix}Parameter(\$stmt, \$php_var='$var', \$name='$name', \$maxLen=$maxLen, \$type=$ztype);");
926
			}
927
			return $this->Bind($stmt,$var,$maxLen,$type,$name,$isOutput);
928
	}
929
 
930
	/*
931
	returns query ID if successful, otherwise false
932
	this version supports:
933
 
934
	   1. $db->execute('select * from table');
935
 
936
	   2. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
937
		  $db->execute($prepared_statement, array(1,2,3));
938
 
939
	   3. $db->execute('insert into table (a,b,c) values (:a,:b,:c)',array('a'=>1,'b'=>2,'c'=>3));
940
 
941
	   4. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
942
		  $db->bind($stmt,1); $db->bind($stmt,2); $db->bind($stmt,3); 
943
		  $db->execute($stmt);
944
	*/ 
945
	function _query($sql,$inputarr)
946
	{
947
		if (is_array($sql)) { // is prepared sql
948
			$stmt = $sql[1];
949
 
950
			// we try to bind to permanent array, so that OCIBindByName is persistent
951
			// and carried out once only - note that max array element size is 4000 chars
952
			if (is_array($inputarr)) {
953
				$bindpos = $sql[3];
954
				if (isset($this->_bind[$bindpos])) {
955
				// all tied up already
956
					$bindarr = &$this->_bind[$bindpos];
957
				} else {
958
				// one statement to bind them all
959
					$bindarr = array();
960
					foreach($inputarr as $k => $v) {
961
						$bindarr[$k] = $v;
962
						OCIBindByName($stmt,":$k",$bindarr[$k],is_string($v) && strlen($v)>4000 ? -1 : 4000);
963
					}
964
					$this->_bind[$bindpos] = &$bindarr;
965
				}
966
			}
967
		} else {
968
			$stmt=OCIParse($this->_connectionID,$sql);
969
		}
970
 
971
		$this->_stmt = $stmt;
972
		if (!$stmt) return false;
973
 
974
		if (defined('ADODB_PREFETCH_ROWS')) @OCISetPrefetch($stmt,ADODB_PREFETCH_ROWS);
975
 
976
		if (is_array($inputarr)) {
977
			foreach($inputarr as $k => $v) {
978
				if (is_array($v)) {
979
					if (sizeof($v) == 2) // suggested by g.giunta@libero.
980
						OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]);
981
					else
982
						OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
983
 
984
					if ($this->debug==99) echo "name=:$k",' var='.$inputarr[$k][0],' len='.$v[1],' type='.$v[2],'<br>';
985
				} else {
986
					$len = -1;
987
					if ($v === ' ') $len = 1;
988
					if (isset($bindarr)) {	// is prepared sql, so no need to ocibindbyname again
989
						$bindarr[$k] = $v;
990
					} else { 				// dynamic sql, so rebind every time
991
						OCIBindByName($stmt,":$k",$inputarr[$k],$len);
992
					}
993
				}
994
			}
995
		}
996
 
997
        $this->_errorMsg = false;
998
		$this->_errorCode = false;
999
		if (OCIExecute($stmt,$this->_commit)) {
1000
//OCIInternalDebug(1);			
1001
			if (count($this -> _refLOBs) > 0) {
1002
 
1003
				foreach ($this -> _refLOBs as $key => $value) {
1004
					if ($this -> _refLOBs[$key]['TYPE'] == true) {
1005
						$tmp = $this -> _refLOBs[$key]['LOB'] -> load();
1006
						if ($this -> debug) {
1007
							ADOConnection::outp("<b>OUT LOB</b>: LOB has been loaded. <br>");
1008
						}
1009
						//$_GLOBALS[$this -> _refLOBs[$key]['VAR']] = $tmp;
1010
						$this -> _refLOBs[$key]['VAR'] = $tmp;
1011
					} else {
1012
                        $this->_refLOBs[$key]['LOB']->save($this->_refLOBs[$key]['VAR']);
1013
						$this -> _refLOBs[$key]['LOB']->free();
1014
						unset($this -> _refLOBs[$key]);
1015
                        if ($this->debug) {
1016
							ADOConnection::outp("<b>IN LOB</b>: LOB has been saved. <br>");
1017
						}
1018
                    }					
1019
				}
1020
			}
1021
 
1022
            switch (@OCIStatementType($stmt)) {
1023
                case "SELECT":
1024
					return $stmt;
1025
 
1026
				case 'DECLARE':
1027
                case "BEGIN":
1028
                    if (is_array($sql) && !empty($sql[4])) {
1029
						$cursor = $sql[4];
1030
						if (is_resource($cursor)) {
1031
							$ok = OCIExecute($cursor);	
1032
	                        return $cursor;
1033
						}
1034
						return $stmt;
1035
                    } else {
1036
						if (is_resource($stmt)) {
1037
							OCIFreeStatement($stmt);
1038
							return true;
1039
						}
1040
                        return $stmt;
1041
                    }
1042
                    break;
1043
                default :
1044
					// ociclose -- no because it could be used in a LOB?
1045
                    return true;
1046
            }
1047
		}
1048
		return false;
1049
	}
1050
 
1051
	// returns true or false
1052
	function _close()
1053
	{
1054
		if (!$this->_connectionID) return;
1055
 
1056
		if (!$this->autoCommit) OCIRollback($this->_connectionID);
1057
		if (count($this->_refLOBs) > 0) {
1058
			foreach ($this ->_refLOBs as $key => $value) {
1059
				$this->_refLOBs[$key]['LOB']->free();
1060
				unset($this->_refLOBs[$key]);
1061
			}
1062
		}
1063
		OCILogoff($this->_connectionID);
1064
 
1065
		$this->_stmt = false;
1066
		$this->_connectionID = false;
1067
	}
1068
 
1069
	function MetaPrimaryKeys($table, $owner=false,$internalKey=false)
1070
	{
1071
		if ($internalKey) return array('ROWID');
1072
 
1073
	// tested with oracle 8.1.7
1074
		$table = strtoupper($table);
1075
		if ($owner) {
1076
			$owner_clause = "AND ((a.OWNER = b.OWNER) AND (a.OWNER = UPPER('$owner')))";
1077
			$ptab = 'ALL_';
1078
		} else {
1079
			$owner_clause = '';
1080
			$ptab = 'USER_';
1081
		}
1082
		$sql = "
1083
SELECT /*+ RULE */ distinct b.column_name
1084
   FROM {$ptab}CONSTRAINTS a
1085
	  , {$ptab}CONS_COLUMNS b
1086
  WHERE ( UPPER(b.table_name) = ('$table'))
1087
	AND (UPPER(a.table_name) = ('$table') and a.constraint_type = 'P')
1088
	$owner_clause
1089
	AND (a.constraint_name = b.constraint_name)";
1090
 
1091
 		$rs = $this->Execute($sql);
1092
		if ($rs && !$rs->EOF) {
1093
			$arr =& $rs->GetArray();
1094
			$a = array();
1095
			foreach($arr as $v) {
1096
				$a[] = reset($v);
1097
			}
1098
			return $a;
1099
		}
1100
		else return false;
1101
	}
1102
 
1103
	// http://gis.mit.edu/classes/11.521/sqlnotes/referential_integrity.html
1104
	function MetaForeignKeys($table, $owner=false)
1105
	{
1106
	global $ADODB_FETCH_MODE;
1107
 
1108
		$save = $ADODB_FETCH_MODE;
1109
		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
1110
		$table = $this->qstr(strtoupper($table));
1111
		if (!$owner) {
1112
			$owner = $this->user;
1113
			$tabp = 'user_';
1114
		} else
1115
			$tabp = 'all_';
1116
 
1117
		$owner = ' and owner='.$this->qstr(strtoupper($owner));
1118
 
1119
		$sql = 
1120
"select constraint_name,r_owner,r_constraint_name 
1121
	from {$tabp}constraints
1122
	where constraint_type = 'R' and table_name = $table $owner";
1123
 
1124
		$constraints =& $this->GetArray($sql);
1125
		$arr = false;
1126
		foreach($constraints as $constr) {
1127
			$cons = $this->qstr($constr[0]);
1128
			$rowner = $this->qstr($constr[1]);
1129
			$rcons = $this->qstr($constr[2]);
1130
			$cols = $this->GetArray("select column_name from {$tabp}cons_columns where constraint_name=$cons $owner order by position");
1131
			$tabcol = $this->GetArray("select table_name,column_name from {$tabp}cons_columns where owner=$rowner and constraint_name=$rcons order by position");
1132
 
1133
			if ($cols && $tabcol) 
1134
				for ($i=0, $max=sizeof($cols); $i < $max; $i++) {
1135
					$arr[$tabcol[$i][0]] = $cols[$i][0].'='.$tabcol[$i][1];
1136
				}
1137
		}
1138
		$ADODB_FETCH_MODE = $save;
1139
 
1140
		return $arr;
1141
	}
1142
 
1143
 
1144
	function CharMax()
1145
	{
1146
		return 4000;
1147
	}
1148
 
1149
	function TextMax()
1150
	{
1151
		return 4000;
1152
	}
1153
 
1154
	/**
1155
	 * Quotes a string.
1156
	 * An example is  $db->qstr("Don't bother",magic_quotes_runtime());
1157
	 * 
1158
	 * @param s			the string to quote
1159
	 * @param [magic_quotes]	if $s is GET/POST var, set to get_magic_quotes_gpc().
1160
	 *				This undoes the stupidity of magic quotes for GPC.
1161
	 *
1162
	 * @return  quoted string to be sent back to database
1163
	 */
1164
	function qstr($s,$magic_quotes=false)
1165
	{	
1166
		//$nofixquotes=false;
1167
 
1168
		if ($this->noNullStrings && strlen($s)==0)$s = ' ';
1169
		if (!$magic_quotes) {	
1170
			if ($this->replaceQuote[0] == '\\'){
1171
				$s = str_replace('\\','\\\\',$s);
1172
			}
1173
			return  "'".str_replace("'",$this->replaceQuote,$s)."'";
1174
		}
1175
 
1176
		// undo magic quotes for "
1177
		$s = str_replace('\\"','"',$s);
1178
 
1179
		$s = str_replace('\\\\','\\',$s);
1180
		return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
1181
 
1182
	}
1183
 
1184
}
1185
 
1186
/*--------------------------------------------------------------------------------------
1187
		 Class Name: Recordset
1188
--------------------------------------------------------------------------------------*/
1189
 
1190
class ADORecordset_oci8 extends ADORecordSet {
1191
 
1192
	var $databaseType = 'oci8';
1193
	var $bind=false;
1194
	var $_fieldobjs;
1195
 
1196
	//var $_arr = false;
1197
 
1198
	function ADORecordset_oci8($queryID,$mode=false)
1199
	{
1200
		if ($mode === false) { 
1201
			global $ADODB_FETCH_MODE;
1202
			$mode = $ADODB_FETCH_MODE;
1203
		}
1204
		switch ($mode)
1205
		{
1206
		case ADODB_FETCH_ASSOC:$this->fetchMode = OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
1207
		case ADODB_FETCH_DEFAULT:
1208
		case ADODB_FETCH_BOTH:$this->fetchMode = OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
1209
		case ADODB_FETCH_NUM: 
1210
		default:
1211
		$this->fetchMode = OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
1212
		}
1213
 
1214
		$this->adodbFetchMode = $mode;
1215
		$this->_queryID = $queryID;
1216
	}
1217
 
1218
 
1219
	function Init()
1220
	{
1221
		if ($this->_inited) return;
1222
 
1223
		$this->_inited = true;
1224
		if ($this->_queryID) {
1225
 
1226
			$this->_currentRow = 0;
1227
			@$this->_initrs();
1228
			$this->EOF = !$this->_fetch();
1229
 
1230
			/*
1231
			// based on idea by Gaetano Giunta to detect unusual oracle errors
1232
			// see http://phplens.com/lens/lensforum/msgs.php?id=6771
1233
			$err = OCIError($this->_queryID);
1234
			if ($err && $this->connection->debug) ADOConnection::outp($err);
1235
			*/
1236
 
1237
			if (!is_array($this->fields)) {
1238
				$this->_numOfRows = 0;
1239
				$this->fields = array();
1240
			}
1241
		} else {
1242
			$this->fields = array();
1243
			$this->_numOfRows = 0;
1244
			$this->_numOfFields = 0;
1245
			$this->EOF = true;
1246
		}
1247
	}
1248
 
1249
	function _initrs()
1250
	{
1251
		$this->_numOfRows = -1;
1252
		$this->_numOfFields = OCInumcols($this->_queryID);
1253
		if ($this->_numOfFields>0) {
1254
			$this->_fieldobjs = array();
1255
			$max = $this->_numOfFields;
1256
			for ($i=0;$i<$max; $i++) $this->_fieldobjs[] = $this->_FetchField($i);
1257
		}
1258
	}
1259
 
1260
	  /*		Returns: an object containing field information.
1261
			  Get column information in the Recordset object. fetchField() can be used in order to obtain information about
1262
			  fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
1263
			  fetchField() is retrieved.		*/
1264
 
1265
	function &_FetchField($fieldOffset = -1)
1266
	{
1267
		$fld = new ADOFieldObject;
1268
		$fieldOffset += 1;
1269
		$fld->name =OCIcolumnname($this->_queryID, $fieldOffset);
1270
		$fld->type = OCIcolumntype($this->_queryID, $fieldOffset);
1271
		$fld->max_length = OCIcolumnsize($this->_queryID, $fieldOffset);
1272
	 	if ($fld->type == 'NUMBER') {
1273
	 		$p = OCIColumnPrecision($this->_queryID, $fieldOffset);
1274
			$sc = OCIColumnScale($this->_queryID, $fieldOffset);
1275
			if ($p != 0 && $sc == 0) $fld->type = 'INT';
1276
			//echo " $this->name ($p.$sc) ";
1277
	 	}
1278
		return $fld;
1279
	}
1280
 
1281
	/* For some reason, OCIcolumnname fails when called after _initrs() so we cache it */
1282
	function &FetchField($fieldOffset = -1)
1283
	{
1284
		return $this->_fieldobjs[$fieldOffset];
1285
	}
1286
 
1287
 
1288
	/*
1289
	// 10% speedup to move MoveNext to child class
1290
	function _MoveNext() 
1291
	{
1292
	//global $ADODB_EXTENSION;if ($ADODB_EXTENSION) return @adodb_movenext($this);
1293
 
1294
		if ($this->EOF) return false;
1295
 
1296
		$this->_currentRow++;
1297
		if(@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode))
1298
			return true;
1299
		$this->EOF = true;
1300
 
1301
		return false;
1302
	}	*/
1303
 
1304
 
1305
	function MoveNext()
1306
	{
1307
		if (@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) {
1308
			$this->_currentRow += 1;
1309
			return true;
1310
		}
1311
		if (!$this->EOF) {
1312
			$this->_currentRow += 1;
1313
			$this->EOF = true;
1314
		}
1315
		return false;
1316
	}
1317
 
1318
	/*
1319
	# does not work as first record is retrieved in _initrs(), so is not included in GetArray()
1320
	function &GetArray($nRows = -1) 
1321
	{
1322
	global $ADODB_OCI8_GETARRAY;
1323
 
1324
		if (true ||  !empty($ADODB_OCI8_GETARRAY)) {
1325
			# does not support $ADODB_ANSI_PADDING_OFF
1326
 
1327
			//OCI_RETURN_NULLS and OCI_RETURN_LOBS is set by OCIfetchstatement
1328
			switch($this->adodbFetchMode) {
1329
			case ADODB_FETCH_NUM:
1330
 
1331
				$ncols = @OCIfetchstatement($this->_queryID, $results, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW+OCI_NUM);
1332
				$results = array_merge(array($this->fields),$results);
1333
				return $results;
1334
 
1335
			case ADODB_FETCH_ASSOC: 
1336
				if (ADODB_ASSOC_CASE != 2 || $this->databaseType != 'oci8') break;
1337
 
1338
				$ncols = @OCIfetchstatement($this->_queryID, $assoc, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW);
1339
				$results =& array_merge(array($this->fields),$assoc);
1340
				return $results;
1341
 
1342
			default:
1343
				break;
1344
			}
1345
		}
1346
 
1347
		$results =& ADORecordSet::GetArray($nRows);
1348
		return $results;
1349
 
1350
	} */
1351
 
1352
	/* Optimize SelectLimit() by using OCIFetch() instead of OCIFetchInto() */
1353
	function &GetArrayLimit($nrows,$offset=-1) 
1354
	{
1355
		if ($offset <= 0) {
1356
			$arr =& $this->GetArray($nrows);
1357
			return $arr;
1358
		}
1359
		for ($i=1; $i < $offset; $i++) 
1360
			if (!@OCIFetch($this->_queryID)) return array();
1361
 
1362
		if (!@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) return array();
1363
		$results = array();
1364
		$cnt = 0;
1365
		while (!$this->EOF && $nrows != $cnt) {
1366
			$results[$cnt++] = $this->fields;
1367
			$this->MoveNext();
1368
		}
1369
 
1370
		return $results;
1371
	}
1372
 
1373
 
1374
	/* Use associative array to get fields array */
1375
	function Fields($colname)
1376
	{
1377
		if (!$this->bind) {
1378
			$this->bind = array();
1379
			for ($i=0; $i < $this->_numOfFields; $i++) {
1380
				$o = $this->FetchField($i);
1381
				$this->bind[strtoupper($o->name)] = $i;
1382
			}
1383
		}
1384
 
1385
		 return $this->fields[$this->bind[strtoupper($colname)]];
1386
	}
1387
 
1388
 
1389
 
1390
	function _seek($row)
1391
	{
1392
		return false;
1393
	}
1394
 
1395
	function _fetch() 
1396
	{
1397
		return @OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode);
1398
	}
1399
 
1400
	/*		close() only needs to be called if you are worried about using too much memory while your script
1401
			is running. All associated result memory for the specified result identifier will automatically be freed.		*/
1402
 
1403
	function _close() 
1404
	{
1405
		if ($this->connection->_stmt === $this->_queryID) $this->connection->_stmt = false;
1406
		if (!empty($this->_refcursor)) {
1407
			OCIFreeCursor($this->_refcursor);
1408
			$this->_refcursor = false;
1409
		}
1410
		@OCIFreeStatement($this->_queryID);
1411
 		$this->_queryID = false;
1412
 
1413
	}
1414
 
1415
	function MetaType($t,$len=-1)
1416
	{
1417
		if (is_object($t)) {
1418
			$fieldobj = $t;
1419
			$t = $fieldobj->type;
1420
			$len = $fieldobj->max_length;
1421
		}
1422
		switch (strtoupper($t)) {
1423
	 	case 'VARCHAR':
1424
	 	case 'VARCHAR2':
1425
		case 'CHAR':
1426
		case 'VARBINARY':
1427
		case 'BINARY':
1428
		case 'NCHAR':
1429
		case 'NVARCHAR':
1430
		case 'NVARCHAR2':
1431
				 if (isset($this) && $len <= $this->blobSize) return 'C';
1432
 
1433
		case 'NCLOB':
1434
		case 'LONG':
1435
		case 'LONG VARCHAR':
1436
		case 'CLOB':
1437
		return 'X';
1438
 
1439
		case 'LONG RAW':
1440
		case 'LONG VARBINARY':
1441
		case 'BLOB':
1442
			return 'B';
1443
 
1444
		case 'DATE': 
1445
			return  ($this->connection->datetime) ? 'T' : 'D';
1446
 
1447
 
1448
		case 'TIMESTAMP': return 'T';
1449
 
1450
		case 'INT': 
1451
		case 'SMALLINT':
1452
		case 'INTEGER': 
1453
			return 'I';
1454
 
1455
		default: return 'N';
1456
		}
1457
	}
1458
}
1459
 
1460
class ADORecordSet_ext_oci8 extends ADORecordSet_oci8 {	
1461
	function ADORecordSet_ext_oci8($queryID,$mode=false) 
1462
	{
1463
		if ($mode === false) { 
1464
			global $ADODB_FETCH_MODE;
1465
			$mode = $ADODB_FETCH_MODE;
1466
		}
1467
		switch ($mode)
1468
		{
1469
		case ADODB_FETCH_ASSOC:$this->fetchMode = OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
1470
		case ADODB_FETCH_DEFAULT:
1471
		case ADODB_FETCH_BOTH:$this->fetchMode = OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
1472
		case ADODB_FETCH_NUM: 
1473
		default: $this->fetchMode = OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
1474
		}
1475
		$this->adodbFetchMode = $mode;
1476
		$this->_queryID = $queryID;
1477
	}
1478
 
1479
	function MoveNext()
1480
	{
1481
		return adodb_movenext($this);
1482
	}
1483
}
1484
?>