Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
/**
3
 * FileDescription
4
 *
5
 * This file is part of ADOdb, a Database Abstraction Layer library for PHP.
6
 *
7
 * @package ADOdb
8
 * @link https://adodb.org Project's web site and documentation
9
 * @link https://github.com/ADOdb/ADOdb Source code and issue tracker
10
 *
11
 * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause
12
 * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option,
13
 * any later version. This means you can use it in proprietary products.
14
 * See the LICENSE.md file distributed with this source code for details.
15
 * @license BSD-3-Clause
16
 * @license LGPL-2.1-or-later
17
 *
18
 * @copyright 2000-2013 John Lim
19
 * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community
20
 * @author John Lim
21
 * @author George Fourlanos <fou@infomap.gr>
22
 */
23
 
24
// security - hide paths
25
if (!defined('ADODB_DIR')) die();
26
 
27
/*
28
NLS_Date_Format
29
Allows you to use a date format other than the Oracle Lite default. When a literal
30
character string appears where a date value is expected, the Oracle Lite database
31
tests the string to see if it matches the formats of Oracle, SQL-92, or the value
32
specified for this parameter in the POLITE.INI file. Setting this parameter also
33
defines the default format used in the TO_CHAR or TO_DATE functions when no
34
other format string is supplied.
35
 
36
For Oracle the default is dd-mon-yy or dd-mon-yyyy, and for SQL-92 the default is
37
yy-mm-dd or yyyy-mm-dd.
38
 
39
Using 'RR' in the format forces two-digit years less than or equal to 49 to be
40
interpreted as years in the 21st century (2000-2049), and years over 50 as years in
41
the 20th century (1950-1999). Setting the RR format as the default for all two-digit
42
year entries allows you to become year-2000 compliant. For example:
43
NLS_DATE_FORMAT='RR-MM-DD'
44
 
45
You can also modify the date format using the ALTER SESSION command.
46
*/
47
 
48
# define the LOB descriptor type for the given type
49
# returns false if no LOB descriptor
50
function oci_lob_desc($type) {
51
	switch ($type) {
52
		case OCI_B_BFILE:  return OCI_D_FILE;
53
		case OCI_B_CFILEE: return OCI_D_FILE;
54
		case OCI_B_CLOB:   return OCI_D_LOB;
55
		case OCI_B_BLOB:   return OCI_D_LOB;
56
		case OCI_B_ROWID:  return OCI_D_ROWID;
57
	}
58
	return false;
59
}
60
 
61
class ADODB_oci8 extends ADOConnection {
62
	var $databaseType = 'oci8';
63
	var $dataProvider = 'oci8';
64
	var $replaceQuote = "''"; // string to use to replace quotes
65
	var $concat_operator='||';
66
	var $sysDate = "TRUNC(SYSDATE)";
67
	var $sysTimeStamp = 'SYSDATE'; // requires oracle 9 or later, otherwise use SYSDATE
68
	var $metaDatabasesSQL = "SELECT USERNAME FROM ALL_USERS WHERE USERNAME NOT IN ('SYS','SYSTEM','DBSNMP','OUTLN') ORDER BY 1";
69
	var $_stmt;
70
	var $_commit = OCI_COMMIT_ON_SUCCESS;
71
	var $_initdate = true; // init date to YYYY-MM-DD
72
	var $metaTablesSQL = "select table_name,table_type from cat where table_type in ('TABLE','VIEW') and table_name not like 'BIN\$%'"; // bin$ tables are recycle bin tables
73
	var $metaColumnsSQL = "select cname,coltype,width, SCALE, PRECISION, NULLS, DEFAULTVAL from col where tname='%s' order by colno"; //changed by smondino@users.sourceforge. net
74
	var $metaColumnsSQL2 = "select column_name,data_type,data_length, data_scale, data_precision,
75
    case when nullable = 'Y' then 'NULL'
76
    else 'NOT NULL' end as nulls,
77
    data_default from all_tab_cols
78
  where owner='%s' and table_name='%s' order by column_id"; // when there is a schema
79
	var $_bindInputArray = true;
80
	var $hasGenID = true;
81
	var $_genIDSQL = "SELECT (%s.nextval) FROM DUAL";
82
	var $_genSeqSQL = "
83
DECLARE
84
	PRAGMA AUTONOMOUS_TRANSACTION;
85
BEGIN
86
	execute immediate 'CREATE SEQUENCE %s START WITH %s';
87
END;
88
";
89
 
90
	var $_dropSeqSQL = "DROP SEQUENCE %s";
91
	var $hasAffectedRows = true;
92
	var $random = "abs(mod(DBMS_RANDOM.RANDOM,10000001)/10000000)";
93
	var $noNullStrings = false;
94
	var $connectSID = false;
95
	var $_bind = array();
96
	var $_nestedSQL = true;
97
	var $_getarray = false; // currently not working
98
	var $leftOuter = '';  // oracle wierdness, $col = $value (+) for LEFT OUTER, $col (+)= $value for RIGHT OUTER
99
	var $session_sharing_force_blob = false; // alter session on updateblob if set to true
100
	var $firstrows = true; // enable first rows optimization on SelectLimit()
101
	var $selectOffsetAlg1 = 1000; // when to use 1st algorithm of selectlimit.
102
	var $NLS_DATE_FORMAT = 'YYYY-MM-DD';  // To include time, use 'RRRR-MM-DD HH24:MI:SS'
103
	var $dateformat = 'YYYY-MM-DD'; // DBDate format
104
	var $useDBDateFormatForTextInput=false;
105
	var $datetime = false; // MetaType('DATE') returns 'D' (datetime==false) or 'T' (datetime == true)
106
	var $_refLOBs = array();
107
 
108
	// var $ansiOuter = true; // if oracle9
109
 
110
	/*
111
	 * Legacy compatibility for sequence names for emulated auto-increments
112
	 */
113
	public $useCompactAutoIncrements = false;
114
 
115
	/*
116
	 * Defines the schema name for emulated auto-increment columns
117
	 */
118
	public $schema = false;
119
 
120
	/*
121
	 * Defines the prefix for emulated auto-increment columns
122
	 */
123
	public $seqPrefix = 'SEQ_';
124
 
125
	/*  function MetaColumns($table, $normalize=true) added by smondino@users.sourceforge.net*/
126
	function MetaColumns($table, $normalize=true)
127
	{
128
	global $ADODB_FETCH_MODE;
129
 
130
		$schema = '';
131
		$this->_findschema($table, $schema);
132
 
133
		$save = $ADODB_FETCH_MODE;
134
		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
135
		if ($this->fetchMode !== false) {
136
			$savem = $this->SetFetchMode(false);
137
		}
138
 
139
		if ($schema){
140
			$rs = $this->Execute(sprintf($this->metaColumnsSQL2, strtoupper($schema), strtoupper($table)));
141
		}
142
		else {
143
			$rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table)));
144
		}
145
 
146
		if (isset($savem)) {
147
			$this->SetFetchMode($savem);
148
		}
149
		$ADODB_FETCH_MODE = $save;
150
		if (!$rs) {
151
			return false;
152
		}
153
		$retarr = array();
154
		while (!$rs->EOF) {
155
			$fld = new ADOFieldObject();
156
			$fld->name = $rs->fields[0];
157
			$fld->type = $rs->fields[1];
158
			$fld->max_length = $rs->fields[2];
159
			$fld->scale = $rs->fields[3];
160
			if ($rs->fields[1] == 'NUMBER') {
161
				if ($rs->fields[3] == 0) {
162
					$fld->type = 'INT';
163
				}
164
				$fld->max_length = $rs->fields[4];
165
			}
166
			$fld->not_null = (strncmp($rs->fields[5], 'NOT',3) === 0);
167
			$fld->binary = (strpos($fld->type,'BLOB') !== false);
168
			$fld->default_value = $rs->fields[6];
169
 
170
			if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) {
171
				$retarr[] = $fld;
172
			}
173
			else {
174
				$retarr[strtoupper($fld->name)] = $fld;
175
			}
176
			$rs->MoveNext();
177
		}
178
		$rs->Close();
179
		if (empty($retarr)) {
180
			return false;
181
		}
182
		return $retarr;
183
	}
184
 
185
	function Time()
186
	{
187
		$rs = $this->Execute("select TO_CHAR($this->sysTimeStamp,'YYYY-MM-DD HH24:MI:SS') from dual");
188
		if ($rs && !$rs->EOF) {
189
			return $this->UnixTimeStamp(reset($rs->fields));
190
		}
191
 
192
		return false;
193
	}
194
 
195
	/**
196
	 * Multiple modes of connection are supported:
197
	 *
198
	 * a. Local Database
199
	 *    $conn->Connect(false,'scott','tiger');
200
	 *
201
	 * b. From tnsnames.ora
202
	 *    $conn->Connect($tnsname,'scott','tiger');
203
	 *    $conn->Connect(false,'scott','tiger',$tnsname);
204
	 *
205
	 * c. Server + service name
206
	 *    $conn->Connect($serveraddress,'scott,'tiger',$service_name);
207
	 *
208
	 * d. Server + SID
209
	 *    $conn->connectSID = true;
210
	 *    $conn->Connect($serveraddress,'scott,'tiger',$SID);
211
	 *
212
	 * @param string|false $argHostname DB server hostname or TNS name
213
	 * @param string $argUsername
214
	 * @param string $argPassword
215
	 * @param string $argDatabasename Service name, SID (defaults to null)
216
	 * @param int $mode Connection mode, defaults to 0
217
	 *                  (0 = non-persistent, 1 = persistent, 2 = force new connection)
218
	 *
219
	 * @return bool
220
	 */
221
	function _connect($argHostname, $argUsername, $argPassword, $argDatabasename=null, $mode=0)
222
	{
223
		if (!function_exists('oci_pconnect')) {
224
			return null;
225
		}
226
		#adodb_backtrace();
227
 
228
		$this->_errorMsg = false;
229
		$this->_errorCode = false;
230
 
231
		if($argHostname) { // added by Jorma Tuomainen <jorma.tuomainen@ppoy.fi>
232
			if (empty($argDatabasename)) {
233
				$argDatabasename = $argHostname;
234
			}
235
			else {
236
				if(strpos($argHostname,":")) {
237
					$argHostinfo=explode(":",$argHostname);
238
					$argHostname=$argHostinfo[0];
239
					$argHostport=$argHostinfo[1];
240
				} else {
241
					$argHostport = empty($this->port)?  "1521" : $this->port;
242
				}
243
 
244
				if (strncasecmp($argDatabasename,'SID=',4) == 0) {
245
					$argDatabasename = substr($argDatabasename,4);
246
					$this->connectSID = true;
247
				}
248
 
249
				if ($this->connectSID) {
250
					$argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
251
					.")(PORT=$argHostport))(CONNECT_DATA=(SID=$argDatabasename)))";
252
				} else
253
					$argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
254
					.")(PORT=$argHostport))(CONNECT_DATA=(SERVICE_NAME=$argDatabasename)))";
255
			}
256
		}
257
 
258
		//if ($argHostname) print "<p>Connect: 1st argument should be left blank for $this->databaseType</p>";
259
		if ($mode==1) {
260
			$this->_connectionID = ($this->charSet)
261
				? oci_pconnect($argUsername,$argPassword, $argDatabasename,$this->charSet)
262
				: oci_pconnect($argUsername,$argPassword, $argDatabasename);
263
			if ($this->_connectionID && $this->autoRollback)  {
264
				oci_rollback($this->_connectionID);
265
			}
266
		} else if ($mode==2) {
267
			$this->_connectionID = ($this->charSet)
268
				? oci_new_connect($argUsername,$argPassword, $argDatabasename,$this->charSet)
269
				: oci_new_connect($argUsername,$argPassword, $argDatabasename);
270
		} else {
271
			$this->_connectionID = ($this->charSet)
272
				? oci_connect($argUsername,$argPassword, $argDatabasename,$this->charSet)
273
				: oci_connect($argUsername,$argPassword, $argDatabasename);
274
		}
275
		if (!$this->_connectionID) {
276
			return false;
277
		}
278
 
279
		if ($this->_initdate) {
280
			$this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='".$this->NLS_DATE_FORMAT."'");
281
		}
282
 
283
		// looks like:
284
		// Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production With the Partitioning option JServer Release 8.1.7.0.0 - Production
285
		// $vers = oci_server_version($this->_connectionID);
286
		// if (strpos($vers,'8i') !== false) $this->ansiOuter = true;
287
		return true;
288
	}
289
 
290
	function ServerInfo()
291
	{
292
		$arr['compat'] = $this->GetOne('select value from sys.database_compatible_level');
293
		$arr['description'] = @oci_server_version($this->_connectionID);
294
		$arr['version'] = ADOConnection::_findvers($arr['description']);
295
		return $arr;
296
	}
297
		// returns true or false
298
	function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
299
	{
300
		return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,1);
301
	}
302
 
303
	// returns true or false
304
	function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
305
	{
306
		return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,2);
307
	}
308
 
309
	function _affectedrows()
310
	{
311
		if (is_resource($this->_stmt)) {
312
			return @oci_num_rows($this->_stmt);
313
		}
314
		return 0;
315
	}
316
 
317
	function IfNull( $field, $ifNull )
318
	{
319
		return " NVL($field, $ifNull) "; // if Oracle
320
	}
321
 
322
	protected function _insertID($table = '', $column = '')
323
	{
324
		if ($this->schema)
325
		{
326
			$t = strpos($table,'.');
327
			if ($t !== false)
328
				$tab = substr($table,$t+1);
329
			else
330
				$tab = $table;
331
 
332
			if ($this->useCompactAutoIncrements)
333
				$tab = sprintf('%u',crc32(strtolower($tab)));
334
 
335
			$seqname = $this->schema.'.'.$this->seqPrefix.$tab;
336
		}
337
		else
338
		{
339
			if ($this->useCompactAutoIncrements)
340
				$table = sprintf('%u',crc32(strtolower($table)));
341
 
342
			$seqname = $this->seqPrefix.$table;
343
		}
344
 
345
		if (strlen($seqname) > 30)
346
			/*
347
			* We cannot successfully identify the sequence
348
			*/
349
			return false;
350
 
351
		return $this->getOne("SELECT $seqname.currval FROM dual");
352
	}
353
 
354
	// format and return date string in database date format
355
	function DBDate($d,$isfld=false)
356
	{
357
		if (empty($d) && $d !== 0) {
358
			return 'null';
359
		}
360
 
361
		if ($isfld) {
362
			$d = _adodb_safedate($d);
363
			return 'TO_DATE('.$d.",'".$this->dateformat."')";
364
		}
365
 
366
		if (is_string($d)) {
367
			$d = ADORecordSet::UnixDate($d);
368
		}
369
 
370
		if (is_object($d)) {
371
			$ds = $d->format($this->fmtDate);
372
		}
373
		else {
374
			$ds = adodb_date($this->fmtDate,$d);
375
		}
376
 
377
		return "TO_DATE(".$ds.",'".$this->dateformat."')";
378
	}
379
 
380
	function BindDate($d)
381
	{
382
		$d = ADOConnection::DBDate($d);
383
		if (strncmp($d, "'", 1)) {
384
			return $d;
385
		}
386
 
387
		return substr($d, 1, strlen($d)-2);
388
	}
389
 
390
	function BindTimeStamp($ts)
391
	{
392
		if (empty($ts) && $ts !== 0) {
393
			return 'null';
394
		}
395
		if (is_string($ts)) {
396
			$ts = ADORecordSet::UnixTimeStamp($ts);
397
		}
398
 
399
		if (is_object($ts)) {
400
			$tss = $ts->format("'Y-m-d H:i:s'");
401
		}
402
		else {
403
			$tss = adodb_date("'Y-m-d H:i:s'",$ts);
404
		}
405
 
406
		return $tss;
407
	}
408
 
409
	// format and return date string in database timestamp format
410
	function DBTimeStamp($ts,$isfld=false)
411
	{
412
		if (empty($ts) && $ts !== 0) {
413
			return 'null';
414
		}
415
		if ($isfld) {
416
			return 'TO_DATE(substr('.$ts.",1,19),'RRRR-MM-DD, HH24:MI:SS')";
417
		}
418
		if (is_string($ts)) {
419
			$ts = ADORecordSet::UnixTimeStamp($ts);
420
		}
421
 
422
		if (is_object($ts)) {
423
			$tss = $ts->format("'Y-m-d H:i:s'");
424
		}
425
		else {
426
			$tss = date("'Y-m-d H:i:s'",$ts);
427
		}
428
 
429
		return 'TO_DATE('.$tss.",'RRRR-MM-DD, HH24:MI:SS')";
430
	}
431
 
432
	function RowLock($tables,$where,$col='1 as adodbignore')
433
	{
434
		if ($this->autoCommit) {
435
			$this->BeginTrans();
436
		}
437
		return $this->GetOne("select $col from $tables where $where for update");
438
	}
439
 
440
	function MetaTables($ttype=false,$showSchema=false,$mask=false)
441
	{
442
		if ($mask) {
443
			$save = $this->metaTablesSQL;
444
			$mask = $this->qstr(strtoupper($mask));
445
			$this->metaTablesSQL .= " AND upper(table_name) like $mask";
446
		}
447
		$ret = ADOConnection::MetaTables($ttype,$showSchema);
448
 
449
		if ($mask) {
450
			$this->metaTablesSQL = $save;
451
		}
452
		return $ret;
453
	}
454
 
455
	// Mark Newnham
456
	function MetaIndexes ($table, $primary = FALSE, $owner=false)
457
	{
458
		// save old fetch mode
459
		global $ADODB_FETCH_MODE;
460
 
461
		$save = $ADODB_FETCH_MODE;
462
		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
463
 
464
		if ($this->fetchMode !== FALSE) {
465
			$savem = $this->SetFetchMode(FALSE);
466
		}
467
 
468
		// get index details
469
		$table = strtoupper($table);
470
 
471
		// get Primary index
472
		$primary_key = '';
473
 
474
		$rs = $this->Execute(sprintf("SELECT * FROM ALL_CONSTRAINTS WHERE UPPER(TABLE_NAME)='%s' AND CONSTRAINT_TYPE='P'",$table));
475
		if (!is_object($rs)) {
476
			if (isset($savem)) {
477
				$this->SetFetchMode($savem);
478
			}
479
			$ADODB_FETCH_MODE = $save;
480
			return false;
481
		}
482
 
483
		if ($row = $rs->FetchRow()) {
484
			$primary_key = $row[1]; //constraint_name
485
		}
486
 
487
		if ($primary==TRUE && $primary_key=='') {
488
			if (isset($savem)) {
489
				$this->SetFetchMode($savem);
490
			}
491
			$ADODB_FETCH_MODE = $save;
492
			return false; //There is no primary key
493
		}
494
 
495
		$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));
496
 
497
 
498
		if (!is_object($rs)) {
499
			if (isset($savem)) {
500
				$this->SetFetchMode($savem);
501
			}
502
			$ADODB_FETCH_MODE = $save;
503
			return false;
504
		}
505
 
506
		$indexes = array ();
507
		// parse index data into array
508
 
509
		while ($row = $rs->FetchRow()) {
510
			if ($primary && $row[0] != $primary_key) {
511
				continue;
512
			}
513
			if (!isset($indexes[$row[0]])) {
514
				$indexes[$row[0]] = array(
515
					'unique' => ($row[1] == 'UNIQUE'),
516
					'columns' => array()
517
				);
518
			}
519
			$indexes[$row[0]]['columns'][$row[2] - 1] = $row[3];
520
		}
521
 
522
		// sort columns by order in the index
523
		foreach ( array_keys ($indexes) as $index ) {
524
			ksort ($indexes[$index]['columns']);
525
		}
526
 
527
		if (isset($savem)) {
528
			$this->SetFetchMode($savem);
529
			$ADODB_FETCH_MODE = $save;
530
		}
531
		return $indexes;
532
	}
533
 
534
	function BeginTrans()
535
	{
536
		if ($this->transOff) {
537
			return true;
538
		}
539
		$this->transCnt += 1;
540
		$this->autoCommit = false;
541
		$this->_commit = OCI_DEFAULT;
542
 
543
		if ($this->_transmode) {
544
			$ok = $this->Execute("SET TRANSACTION ".$this->_transmode);
545
		}
546
		else {
547
			$ok = true;
548
		}
549
 
550
		return $ok ? true : false;
551
	}
552
 
553
	function CommitTrans($ok=true)
554
	{
555
		if ($this->transOff) {
556
			return true;
557
		}
558
		if (!$ok) {
559
			return $this->RollbackTrans();
560
		}
561
 
562
		if ($this->transCnt) {
563
			$this->transCnt -= 1;
564
		}
565
		$ret = oci_commit($this->_connectionID);
566
		$this->_commit = OCI_COMMIT_ON_SUCCESS;
567
		$this->autoCommit = true;
568
		return $ret;
569
	}
570
 
571
	function RollbackTrans()
572
	{
573
		if ($this->transOff) {
574
			return true;
575
		}
576
		if ($this->transCnt) {
577
			$this->transCnt -= 1;
578
		}
579
		$ret = oci_rollback($this->_connectionID);
580
		$this->_commit = OCI_COMMIT_ON_SUCCESS;
581
		$this->autoCommit = true;
582
		return $ret;
583
	}
584
 
585
 
586
	function SelectDB($dbName)
587
	{
588
		return false;
589
	}
590
 
591
	function ErrorMsg()
592
	{
593
		if ($this->_errorMsg !== false) {
594
			return $this->_errorMsg;
595
		}
596
 
597
		if (is_resource($this->_stmt)) {
598
			$arr = @oci_error($this->_stmt);
599
		}
600
		if (empty($arr)) {
601
			if (is_resource($this->_connectionID)) {
602
				$arr = @oci_error($this->_connectionID);
603
			}
604
			else {
605
				$arr = @oci_error();
606
			}
607
			if ($arr === false) {
608
				return '';
609
			}
610
		}
611
		$this->_errorMsg = $arr['message'];
612
		$this->_errorCode = $arr['code'];
613
		return $this->_errorMsg;
614
	}
615
 
616
	function ErrorNo()
617
	{
618
		if ($this->_errorCode !== false) {
619
			return $this->_errorCode;
620
		}
621
 
622
		if (is_resource($this->_stmt)) {
623
			$arr = @oci_error($this->_stmt);
624
		}
625
		if (empty($arr)) {
626
			$arr = @oci_error($this->_connectionID);
627
			if ($arr == false) {
628
				$arr = @oci_error();
629
			}
630
			if ($arr == false) {
631
				return '';
632
			}
633
		}
634
 
635
		$this->_errorMsg = $arr['message'];
636
		$this->_errorCode = $arr['code'];
637
 
638
		return $arr['code'];
639
	}
640
 
641
	/**
642
	 * Format date column in sql string given an input format that understands Y M D
643
	 */
644
	function SQLDate($fmt, $col=false)
645
	{
646
		if (!$col) {
647
			$col = $this->sysTimeStamp;
648
		}
649
		$s = 'TO_CHAR('.$col.",'";
650
 
651
		$len = strlen($fmt);
652
		for ($i=0; $i < $len; $i++) {
653
			$ch = $fmt[$i];
654
			switch($ch) {
655
			case 'Y':
656
			case 'y':
657
				$s .= 'YYYY';
658
				break;
659
			case 'Q':
660
			case 'q':
661
				$s .= 'Q';
662
				break;
663
 
664
			case 'M':
665
				$s .= 'Mon';
666
				break;
667
 
668
			case 'm':
669
				$s .= 'MM';
670
				break;
671
			case 'D':
672
			case 'd':
673
				$s .= 'DD';
674
				break;
675
 
676
			case 'H':
677
				$s.= 'HH24';
678
				break;
679
 
680
			case 'h':
681
				$s .= 'HH';
682
				break;
683
 
684
			case 'i':
685
				$s .= 'MI';
686
				break;
687
 
688
			case 's':
689
				$s .= 'SS';
690
				break;
691
 
692
			case 'a':
693
			case 'A':
694
				$s .= 'AM';
695
				break;
696
 
697
			case 'w':
698
				$s .= 'D';
699
				break;
700
 
701
			case 'l':
702
				$s .= 'DAY';
703
				break;
704
 
705
			case 'W':
706
				$s .= 'WW';
707
				break;
708
 
709
			default:
710
				// handle escape characters...
711
				if ($ch == '\\') {
712
					$i++;
713
					$ch = substr($fmt,$i,1);
714
				}
715
				if (strpos('-/.:;, ',$ch) !== false) {
716
					$s .= $ch;
717
				}
718
				else {
719
					$s .= '"'.$ch.'"';
720
				}
721
 
722
			}
723
		}
724
		return $s. "')";
725
	}
726
 
727
	function GetRandRow($sql, $arr = false)
728
	{
729
		$sql = "SELECT * FROM ($sql ORDER BY dbms_random.value) WHERE rownum = 1";
730
 
731
		return $this->GetRow($sql,$arr);
732
	}
733
 
734
	/**
735
	 * This algorithm makes use of
736
	 *
737
	 * a. FIRST_ROWS hint
738
	 * The FIRST_ROWS hint explicitly chooses the approach to optimize response
739
	 * time, that is, minimum resource usage to return the first row. Results
740
	 * will be returned as soon as they are identified.
741
	 *
742
	 * b. Uses rownum tricks to obtain only the required rows from a given offset.
743
	 * As this uses complicated sql statements, we only use this if $offset >= 100.
744
	 * This idea by Tomas V V Cox.
745
	 *
746
	 * This implementation does not appear to work with oracle 8.0.5 or earlier.
747
	 * Comment out this function then, and the slower SelectLimit() in the base
748
	 * class will be used.
749
	 *
750
	 * Note: FIRST_ROWS hinting is only used if $sql is a string; when
751
	 * processing a prepared statement's handle, no hinting is performed.
752
	 */
753
	function SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
754
	{
755
		$nrows = (int) $nrows;
756
		$offset = (int) $offset;
757
		// Since the methods used to limit the number of returned rows rely
758
		// on modifying the provided SQL query, we can't work with prepared
759
		// statements so we just extract the SQL string.
760
		if(is_array($sql)) {
761
			$sql = $sql[0];
762
		}
763
 
764
		// seems that oracle only supports 1 hint comment in 8i
765
		if ($this->firstrows) {
766
			if ($nrows > 500 && $nrows < 1000) {
767
				$hint = "FIRST_ROWS($nrows)";
768
			}
769
			else {
770
				$hint = 'FIRST_ROWS';
771
			}
772
 
773
			if (strpos($sql,'/*+') !== false) {
774
				$sql = str_replace('/*+ ',"/*+$hint ",$sql);
775
			}
776
			else {
777
				$sql = preg_replace('/^[ \t\n]*select/i',"SELECT /*+$hint*/",$sql);
778
			}
779
			$hint = "/*+ $hint */";
780
		} else {
781
			$hint = '';
782
		}
783
 
784
		// If non-bound statement, $inputarr is false
785
		if (!$inputarr) {
786
			$inputarr = array();
787
		}
788
 
789
		if ($offset == -1 || ($offset < $this->selectOffsetAlg1 && 0 < $nrows && $nrows < 1000)) {
790
			if ($nrows > 0) {
791
				if ($offset > 0) {
792
					$nrows += $offset;
793
				}
794
				$sql = "select * from (".$sql.") where rownum <= :adodb_offset";
795
 
796
				$inputarr['adodb_offset'] = $nrows;
797
				$nrows = -1;
798
			}
799
			// note that $nrows = 0 still has to work ==> no rows returned
800
 
801
			return ADOConnection::SelectLimit($sql, $nrows, $offset, $inputarr, $secs2cache);
802
		} else {
803
			// Algorithm by Tomas V V Cox, from PEAR DB oci8.php
804
 
805
			// Let Oracle return the name of the columns
806
			$q_fields = "SELECT * FROM (".$sql.") WHERE NULL = NULL";
807
 
808
			if (! $stmt_arr = $this->Prepare($q_fields)) {
809
				return false;
810
			}
811
			$stmt = $stmt_arr[1];
812
 
813
			foreach($inputarr as $k => $v) {
814
				$i = 0;
815
				if ($this->databaseType == 'oci8po') {
816
					$bv_name = ":" . $i++;
817
				} else {
818
					$bv_name = ":" . $k;
819
				}
820
				if (is_array($v)) {
821
					// suggested by g.giunta@libero.
822
					if (sizeof($v) == 2) {
823
						oci_bind_by_name($stmt, $bv_name, $inputarr[$k][0], $v[1]);
824
					} else {
825
						oci_bind_by_name($stmt, $bv_name, $inputarr[$k][0], $v[1], $v[2]);
826
					}
827
				} else {
828
					$len = -1;
829
					if ($v === ' ') {
830
						$len = 1;
831
					}
832
					if (isset($bindarr)) {
833
						// prepared sql, so no need to oci_bind_by_name again
834
						$bindarr[$k] = $v;
835
					} else {
836
						// dynamic sql, so rebind every time
837
						oci_bind_by_name($stmt, $bv_name, $inputarr[$k], $len);
838
					}
839
				}
840
			}
841
 
842
			if (!oci_execute($stmt, OCI_DEFAULT)) {
843
				oci_free_statement($stmt);
844
				return false;
845
			}
846
 
847
			$ncols = oci_num_fields($stmt);
848
			for ( $i = 1; $i <= $ncols; $i++ ) {
849
				$cols[] = '"'.oci_field_name($stmt, $i).'"';
850
			}
851
			$result = false;
852
 
853
			oci_free_statement($stmt);
854
			$fields = implode(',', $cols);
855
			if ($nrows <= 0) {
856
				$nrows = 999999999999;
857
			}
858
			else {
859
				$nrows += $offset;
860
			}
861
			$offset += 1; // in Oracle rownum starts at 1
862
 
863
			$sql = "SELECT $hint $fields FROM".
864
				"(SELECT rownum as adodb_rownum, $fields FROM".
865
				" ($sql) WHERE rownum <= :adodb_nrows".
866
				") WHERE adodb_rownum >= :adodb_offset";
867
			$inputarr['adodb_nrows'] = $nrows;
868
			$inputarr['adodb_offset'] = $offset;
869
 
870
			if ($secs2cache > 0) {
871
				$rs = $this->CacheExecute($secs2cache, $sql,$inputarr);
872
			}
873
			else {
874
				$rs = $this->Execute($sql, $inputarr);
875
			}
876
			return $rs;
877
		}
878
	}
879
 
880
	/**
881
	 * Usage:
882
	 * Store BLOBs and CLOBs
883
	 *
884
	 * Example: to store $var in a blob
885
	 *    $conn->Execute('insert into TABLE (id,ablob) values(12,empty_blob())');
886
	 *    $conn->UpdateBlob('TABLE', 'ablob', $varHoldingBlob, 'ID=12', 'BLOB');
887
	 *
888
	 * $blobtype supports 'BLOB' and 'CLOB', but you need to change to 'empty_clob()'.
889
	 *
890
	 * to get length of LOB:
891
	 *    select DBMS_LOB.GETLENGTH(ablob) from TABLE
892
	 *
893
	 * If you are using CURSOR_SHARING = force, it appears this will case a segfault
894
	 * under oracle 8.1.7.0. Run:
895
	 *    $db->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT');
896
	 * before UpdateBlob() then...
897
	 */
898
	function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
899
	{
900
 
901
		//if (strlen($val) < 4000) return $this->Execute("UPDATE $table SET $column=:blob WHERE $where",array('blob'=>$val)) != false;
902
 
903
		switch(strtoupper($blobtype)) {
904
		default: ADOConnection::outp("<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
905
		case 'BLOB': $type = OCI_B_BLOB; break;
906
		case 'CLOB': $type = OCI_B_CLOB; break;
907
		}
908
 
909
		if ($this->databaseType == 'oci8po')
910
			$sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
911
		else
912
			$sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
913
 
914
		$desc = oci_new_descriptor($this->_connectionID, OCI_D_LOB);
915
		$arr['blob'] = array($desc,-1,$type);
916
		if ($this->session_sharing_force_blob) {
917
			$this->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT');
918
		}
919
		$commit = $this->autoCommit;
920
		if ($commit) {
921
			$this->BeginTrans();
922
		}
923
		$rs = $this->_Execute($sql,$arr);
924
		if ($rez = !empty($rs)) {
925
			$desc->save($val);
926
		}
927
		$desc->free();
928
		if ($commit) {
929
			$this->CommitTrans();
930
		}
931
		if ($this->session_sharing_force_blob) {
932
			$this->Execute('ALTER SESSION SET CURSOR_SHARING=FORCE');
933
		}
934
 
935
		if ($rez) {
936
			$rs->Close();
937
		}
938
		return $rez;
939
	}
940
 
941
	/**
942
	 * Usage:  store file pointed to by $val in a blob
943
	 */
944
	function UpdateBlobFile($table,$column,$val,$where,$blobtype='BLOB')
945
	{
946
		switch(strtoupper($blobtype)) {
947
		default: ADOConnection::outp( "<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
948
		case 'BLOB': $type = OCI_B_BLOB; break;
949
		case 'CLOB': $type = OCI_B_CLOB; break;
950
		}
951
 
952
		if ($this->databaseType == 'oci8po')
953
			$sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
954
		else
955
			$sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
956
 
957
		$desc = oci_new_descriptor($this->_connectionID, OCI_D_LOB);
958
		$arr['blob'] = array($desc,-1,$type);
959
 
960
		$this->BeginTrans();
961
		$rs = ADODB_oci8::Execute($sql,$arr);
962
		if ($rez = !empty($rs)) {
963
			$desc->savefile($val);
964
		}
965
		$desc->free();
966
		$this->CommitTrans();
967
 
968
		if ($rez) {
969
			$rs->Close();
970
		}
971
		return $rez;
972
	}
973
 
974
	function Execute($sql,$inputarr=false)
975
	{
976
		if ($this->fnExecute) {
977
			$fn = $this->fnExecute;
978
			$ret = $fn($this,$sql,$inputarr);
979
			if (isset($ret)) {
980
				return $ret;
981
			}
982
		}
983
		if ($inputarr !== false) {
984
			if (!is_array($inputarr)) {
985
				$inputarr = array($inputarr);
986
			}
987
 
988
			$element0 = reset($inputarr);
989
			$array2d =  $this->bulkBind && is_array($element0) && !is_object(reset($element0));
990
 
991
			# see PHPLens Issue No: 18786
992
			if ($array2d || !$this->_bindInputArray) {
993
 
994
				# is_object check because oci8 descriptors can be passed in
995
				if ($array2d && $this->_bindInputArray) {
996
					if (is_string($sql)) {
997
						$stmt = $this->Prepare($sql);
998
					} else {
999
						$stmt = $sql;
1000
					}
1001
 
1002
					foreach($inputarr as $arr) {
1003
						$ret = $this->_Execute($stmt,$arr);
1004
						if (!$ret) {
1005
							return $ret;
1006
						}
1007
					}
1008
					return $ret;
1009
				} else {
1010
					$sqlarr = explode(':', $sql);
1011
					$sql = '';
1012
					$lastnomatch = -2;
1013
					#var_dump($sqlarr);echo "<hr>";var_dump($inputarr);echo"<hr>";
1014
					foreach($sqlarr as $k => $str) {
1015
						if ($k == 0) {
1016
							$sql = $str;
1017
							continue;
1018
						}
1019
						// we need $lastnomatch because of the following datetime,
1020
						// eg. '10:10:01', which causes code to think that there is bind param :10 and :1
1021
						$ok = preg_match('/^([0-9]*)/', $str, $arr);
1022
 
1023
						if (!$ok) {
1024
							$sql .= $str;
1025
						} else {
1026
							$at = $arr[1];
1027
							if (isset($inputarr[$at]) || is_null($inputarr[$at])) {
1028
								if ((strlen($at) == strlen($str) && $k < sizeof($arr)-1)) {
1029
									$sql .= ':'.$str;
1030
									$lastnomatch = $k;
1031
								} else if ($lastnomatch == $k-1) {
1032
									$sql .= ':'.$str;
1033
								} else {
1034
									if (is_null($inputarr[$at])) {
1035
										$sql .= 'null';
1036
									}
1037
									else {
1038
										$sql .= $this->qstr($inputarr[$at]);
1039
									}
1040
									$sql .= substr($str, strlen($at));
1041
								}
1042
							} else {
1043
								$sql .= ':'.$str;
1044
							}
1045
						}
1046
					}
1047
					$inputarr = false;
1048
				}
1049
			}
1050
			$ret = $this->_Execute($sql,$inputarr);
1051
 
1052
		} else {
1053
			$ret = $this->_Execute($sql,false);
1054
		}
1055
 
1056
		return $ret;
1057
	}
1058
 
1059
	/*
1060
	 * Example of usage:
1061
	 *    $stmt = $this->Prepare('insert into emp (empno, ename) values (:empno, :ename)');
1062
	*/
1063
	function Prepare($sql,$cursor=false)
1064
	{
1065
	static $BINDNUM = 0;
1066
 
1067
		$stmt = oci_parse($this->_connectionID,$sql);
1068
 
1069
		if (!$stmt) {
1070
			$this->_errorMsg = false;
1071
			$this->_errorCode = false;
1072
			$arr = @oci_error($this->_connectionID);
1073
			if ($arr === false) {
1074
				return false;
1075
			}
1076
 
1077
			$this->_errorMsg = $arr['message'];
1078
			$this->_errorCode = $arr['code'];
1079
			return false;
1080
		}
1081
 
1082
		$BINDNUM += 1;
1083
 
1084
		$sttype = @oci_statement_type($stmt);
1085
		if ($sttype == 'BEGIN' || $sttype == 'DECLARE') {
1086
			return array($sql,$stmt,0,$BINDNUM, ($cursor) ? oci_new_cursor($this->_connectionID) : false);
1087
		}
1088
		return array($sql,$stmt,0,$BINDNUM);
1089
	}
1090
 
1091
	function releaseStatement(&$stmt)
1092
	{
1093
		if (is_array($stmt)
1094
			&& isset($stmt[1])
1095
			&& is_resource($stmt[1])
1096
			&& oci_free_statement($stmt[1])
1097
		) {
1098
			// Clearing the resource to avoid it being of type Unknown
1099
			$stmt[1] = null;
1100
			return true;
1101
		}
1102
 
1103
		// Not a valid prepared statement
1104
		return false;
1105
	}
1106
 
1107
	/*
1108
		Call an oracle stored procedure and returns a cursor variable as a recordset.
1109
		Concept by Robert Tuttle robert@ud.com
1110
 
1111
		Example:
1112
			Note: we return a cursor variable in :RS2
1113
			$rs = $db->ExecuteCursor("BEGIN adodb.open_tab(:RS2); END;",'RS2');
1114
 
1115
			$rs = $db->ExecuteCursor(
1116
				"BEGIN :RS2 = adodb.getdata(:VAR1); END;",
1117
				'RS2',
1118
				array('VAR1' => 'Mr Bean'));
1119
 
1120
	*/
1121
	function ExecuteCursor($sql,$cursorName='rs',$params=false)
1122
	{
1123
		if (is_array($sql)) {
1124
			$stmt = $sql;
1125
		}
1126
		else $stmt = ADODB_oci8::Prepare($sql,true); # true to allocate oci_new_cursor
1127
 
1128
		if (is_array($stmt) && sizeof($stmt) >= 5) {
1129
			$hasref = true;
1130
			$ignoreCur = false;
1131
			$this->Parameter($stmt, $ignoreCur, $cursorName, false, -1, OCI_B_CURSOR);
1132
			if ($params) {
1133
				foreach($params as $k => $v) {
1134
					$this->Parameter($stmt,$params[$k], $k);
1135
				}
1136
			}
1137
		} else
1138
			$hasref = false;
1139
 
1140
		/** @var ADORecordset_oci8 $rs */
1141
		$rs = $this->Execute($stmt);
1142
		if ($rs) {
1143
			if ($rs->databaseType == 'array') {
1144
				oci_free_statement($stmt[4]);
1145
			}
1146
			elseif ($hasref) {
1147
				$rs->_refcursor = $stmt[4];
1148
			}
1149
		}
1150
		return $rs;
1151
	}
1152
 
1153
	/**
1154
	 * Bind a variable -- very, very fast for executing repeated statements in oracle.
1155
	 *
1156
	 * Better than using
1157
	 *    for ($i = 0; $i < $max; $i++) {
1158
	 *        $p1 = ?; $p2 = ?; $p3 = ?;
1159
	 *        $this->Execute("insert into table (col0, col1, col2) values (:0, :1, :2)", array($p1,$p2,$p3));
1160
	 *    }
1161
	 *
1162
	 * Usage:
1163
	 *    $stmt = $DB->Prepare("insert into table (col0, col1, col2) values (:0, :1, :2)");
1164
	 *    $DB->Bind($stmt, $p1);
1165
	 *    $DB->Bind($stmt, $p2);
1166
	 *    $DB->Bind($stmt, $p3);
1167
	 *    for ($i = 0; $i < $max; $i++) {
1168
	 *        $p1 = ?; $p2 = ?; $p3 = ?;
1169
	 *        $DB->Execute($stmt);
1170
	 *    }
1171
	 *
1172
	 * Some timings to insert 1000 records, test table has 3 cols, and 1 index.
1173
	 * - Time 0.6081s (1644.60 inserts/sec) with direct oci_parse/oci_execute
1174
	 * - Time 0.6341s (1577.16 inserts/sec) with ADOdb Prepare/Bind/Execute
1175
	 * - Time 1.5533s ( 643.77 inserts/sec) with pure SQL using Execute
1176
	 *
1177
	 * Now if PHP only had batch/bulk updating like Java or PL/SQL...
1178
	 *
1179
	 * Note that the order of parameters differs from oci_bind_by_name,
1180
	 * because we default the names to :0, :1, :2
1181
	 */
1182
	function Bind(&$stmt,&$var,$size=4000,$type=false,$name=false,$isOutput=false)
1183
	{
1184
 
1185
		if (!is_array($stmt)) {
1186
			return false;
1187
		}
1188
 
1189
		if (($type == OCI_B_CURSOR) && sizeof($stmt) >= 5) {
1190
			return oci_bind_by_name($stmt[1],":".$name,$stmt[4],$size,$type);
1191
		}
1192
 
1193
		if ($name == false) {
1194
			if ($type !== false) {
1195
				$rez = oci_bind_by_name($stmt[1],":".$stmt[2],$var,$size,$type);
1196
			}
1197
			else {
1198
				$rez = oci_bind_by_name($stmt[1],":".$stmt[2],$var,$size); // +1 byte for null terminator
1199
			}
1200
			$stmt[2] += 1;
1201
		} else if (oci_lob_desc($type)) {
1202
			if ($this->debug) {
1203
				ADOConnection::outp("<b>Bind</b>: name = $name");
1204
			}
1205
			//we have to create a new Descriptor here
1206
			$numlob = count($this->_refLOBs);
1207
			$this->_refLOBs[$numlob]['LOB'] = oci_new_descriptor($this->_connectionID, oci_lob_desc($type));
1208
			$this->_refLOBs[$numlob]['TYPE'] = $isOutput;
1209
 
1210
			$tmp = $this->_refLOBs[$numlob]['LOB'];
1211
			$rez = oci_bind_by_name($stmt[1], ":".$name, $tmp, -1, $type);
1212
			if ($this->debug) {
1213
				ADOConnection::outp("<b>Bind</b>: descriptor has been allocated, var (".$name.") binded");
1214
			}
1215
 
1216
			// if type is input then write data to lob now
1217
			if ($isOutput == false) {
1218
				$var = $this->BlobEncode($var);
1219
				$tmp->WriteTemporary($var);
1220
				$this->_refLOBs[$numlob]['VAR'] = &$var;
1221
				if ($this->debug) {
1222
					ADOConnection::outp("<b>Bind</b>: LOB has been written to temp");
1223
				}
1224
			} else {
1225
				$this->_refLOBs[$numlob]['VAR'] = &$var;
1226
			}
1227
			$rez = $tmp;
1228
		} else {
1229
			if ($this->debug)
1230
				ADOConnection::outp("<b>Bind</b>: name = $name");
1231
 
1232
			if ($type !== false) {
1233
				$rez = oci_bind_by_name($stmt[1],":".$name,$var,$size,$type);
1234
			}
1235
			else {
1236
				$rez = oci_bind_by_name($stmt[1],":".$name,$var,$size); // +1 byte for null terminator
1237
			}
1238
		}
1239
 
1240
		return $rez;
1241
	}
1242
 
1243
	function Param($name,$type='C')
1244
	{
1245
		return ':'.$name;
1246
	}
1247
 
1248
	/**
1249
	 * Usage:
1250
	 *    $stmt = $db->Prepare('select * from table where id =:myid and group=:group');
1251
	 *    $db->Parameter($stmt,$id,'myid');
1252
	 *    $db->Parameter($stmt,$group,'group');
1253
	 *    $db->Execute($stmt);
1254
	 *
1255
	 * @param $stmt Statement returned by {@see Prepare()} or {@see PrepareSP()}.
1256
	 * @param $var PHP variable to bind to
1257
	 * @param $name Name of stored procedure variable name to bind to.
1258
	 * @param bool $isOutput Indicates direction of parameter 0/false=IN  1=OUT  2= IN/OUT. This is ignored in oci8.
1259
	 * @param int $maxLen Holds an maximum length of the variable.
1260
	 * @param mixed $type The data type of $var. Legal values depend on driver.
1261
	 *
1262
	 * @link http://php.net/oci_bind_by_name
1263
	*/
1264
	function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
1265
	{
1266
			if  ($this->debug) {
1267
				$prefix = ($isOutput) ? 'Out' : 'In';
1268
				$ztype = (empty($type)) ? 'false' : $type;
1269
				ADOConnection::outp( "{$prefix}Parameter(\$stmt, \$php_var='$var', \$name='$name', \$maxLen=$maxLen, \$type=$ztype);");
1270
			}
1271
			return $this->Bind($stmt,$var,$maxLen,$type,$name,$isOutput);
1272
	}
1273
 
1274
	/**
1275
	 * Execute a query.
1276
	 *
1277
	 * this version supports:
1278
	 *
1279
	 * 1. $db->execute('select * from table');
1280
	 *
1281
	 * 2. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
1282
	 *    $db->execute($prepared_statement, array(1,2,3));
1283
	 *
1284
	 * 3. $db->execute('insert into table (a,b,c) values (:a,:b,:c)',array('a'=>1,'b'=>2,'c'=>3));
1285
	 *
1286
	 * 4. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
1287
	 *    $db->bind($stmt,1); $db->bind($stmt,2); $db->bind($stmt,3);
1288
	 *    $db->execute($stmt);
1289
	 *
1290
	 * @param string|array $sql        Query to execute.
1291
	 * @param array        $inputarr   An optional array of parameters.
1292
	 *
1293
	 * @return mixed|bool Query identifier or true if execution successful, false if failed.
1294
	 */
1295
	function _query($sql,$inputarr=false)
1296
	{
1297
		if (is_array($sql)) { // is prepared sql
1298
			$stmt = $sql[1];
1299
 
1300
			// we try to bind to permanent array, so that oci_bind_by_name is persistent
1301
			// and carried out once only - note that max array element size is 4000 chars
1302
			if (is_array($inputarr)) {
1303
				$bindpos = $sql[3];
1304
				if (isset($this->_bind[$bindpos])) {
1305
				// all tied up already
1306
					$bindarr = $this->_bind[$bindpos];
1307
				} else {
1308
				// one statement to bind them all
1309
					$bindarr = array();
1310
					foreach($inputarr as $k => $v) {
1311
						$bindarr[$k] = $v;
1312
						oci_bind_by_name($stmt,":$k",$bindarr[$k],is_string($v) && strlen($v)>4000 ? -1 : 4000);
1313
					}
1314
					$this->_bind[$bindpos] = $bindarr;
1315
				}
1316
			}
1317
		} else {
1318
			$stmt=oci_parse($this->_connectionID,$sql);
1319
		}
1320
 
1321
		$this->_stmt = $stmt;
1322
		if (!$stmt) {
1323
			return false;
1324
		}
1325
 
1326
		if (defined('ADODB_PREFETCH_ROWS')) {
1327
			@oci_set_prefetch($stmt,ADODB_PREFETCH_ROWS);
1328
		}
1329
 
1330
		if (is_array($inputarr)) {
1331
			foreach($inputarr as $k => $v) {
1332
				if (is_array($v)) {
1333
					// suggested by g.giunta@libero.
1334
					if (sizeof($v) == 2) {
1335
						oci_bind_by_name($stmt,":$k",$inputarr[$k][0],$v[1]);
1336
					}
1337
					else {
1338
						oci_bind_by_name($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
1339
					}
1340
 
1341
					if ($this->debug==99) {
1342
						if (is_object($v[0])) {
1343
							echo "name=:$k",' len='.$v[1],' type='.$v[2],'<br>';
1344
						}
1345
						else {
1346
							echo "name=:$k",' var='.$inputarr[$k][0],' len='.$v[1],' type='.$v[2],'<br>';
1347
						}
1348
 
1349
					}
1350
				} else {
1351
					$len = -1;
1352
					if ($v === ' ') {
1353
						$len = 1;
1354
					}
1355
					if (isset($bindarr)) {	// is prepared sql, so no need to oci_bind_by_name again
1356
						$bindarr[$k] = $v;
1357
					} else { 				// dynamic sql, so rebind every time
1358
						oci_bind_by_name($stmt,":$k",$inputarr[$k],$len);
1359
					}
1360
				}
1361
			}
1362
		}
1363
 
1364
		$this->_errorMsg = false;
1365
		$this->_errorCode = false;
1366
		if (oci_execute($stmt,$this->_commit)) {
1367
 
1368
			if (count($this -> _refLOBs) > 0) {
1369
 
1370
				foreach ($this -> _refLOBs as $key => $value) {
1371
					if ($this -> _refLOBs[$key]['TYPE'] == true) {
1372
						$tmp = $this -> _refLOBs[$key]['LOB'] -> load();
1373
						if ($this -> debug) {
1374
							ADOConnection::outp("<b>OUT LOB</b>: LOB has been loaded. <br>");
1375
						}
1376
						//$_GLOBALS[$this -> _refLOBs[$key]['VAR']] = $tmp;
1377
						$this -> _refLOBs[$key]['VAR'] = $tmp;
1378
					} else {
1379
						$this->_refLOBs[$key]['LOB']->save($this->_refLOBs[$key]['VAR']);
1380
						$this -> _refLOBs[$key]['LOB']->free();
1381
						unset($this -> _refLOBs[$key]);
1382
						if ($this->debug) {
1383
							ADOConnection::outp("<b>IN LOB</b>: LOB has been saved. <br>");
1384
						}
1385
					}
1386
				}
1387
			}
1388
 
1389
			switch (@oci_statement_type($stmt)) {
1390
				case "SELECT":
1391
					return $stmt;
1392
 
1393
				case 'DECLARE':
1394
				case "BEGIN":
1395
					if (is_array($sql) && !empty($sql[4])) {
1396
						$cursor = $sql[4];
1397
						if (is_resource($cursor)) {
1398
							$ok = oci_execute($cursor);
1399
							return $cursor;
1400
						}
1401
						return $stmt;
1402
					} else {
1403
						if (is_resource($stmt)) {
1404
							oci_free_statement($stmt);
1405
							return true;
1406
						}
1407
						return $stmt;
1408
					}
1409
					break;
1410
				default :
1411
 
1412
					return true;
1413
			}
1414
		}
1415
		return false;
1416
	}
1417
 
1418
	// From Oracle Whitepaper: PHP Scalability and High Availability
1419
	function IsConnectionError($err)
1420
	{
1421
		switch($err) {
1422
			case 378: /* buffer pool param incorrect */
1423
			case 602: /* core dump */
1424
			case 603: /* fatal error */
1425
			case 609: /* attach failed */
1426
			case 1012: /* not logged in */
1427
			case 1033: /* init or shutdown in progress */
1428
			case 1043: /* Oracle not available */
1429
			case 1089: /* immediate shutdown in progress */
1430
			case 1090: /* shutdown in progress */
1431
			case 1092: /* instance terminated */
1432
			case 3113: /* disconnect */
1433
			case 3114: /* not connected */
1434
			case 3122: /* closing window */
1435
			case 3135: /* lost contact */
1436
			case 12153: /* TNS: not connected */
1437
			case 27146: /* fatal or instance terminated */
1438
			case 28511: /* Lost RPC */
1439
			return true;
1440
		}
1441
		return false;
1442
	}
1443
 
1444
	// returns true or false
1445
	function _close()
1446
	{
1447
		if (!$this->_connectionID) {
1448
			return;
1449
		}
1450
 
1451
 
1452
		if (!$this->autoCommit) {
1453
			oci_rollback($this->_connectionID);
1454
		}
1455
		if (count($this->_refLOBs) > 0) {
1456
			foreach ($this ->_refLOBs as $key => $value) {
1457
				$this->_refLOBs[$key]['LOB']->free();
1458
				unset($this->_refLOBs[$key]);
1459
			}
1460
		}
1461
		oci_close($this->_connectionID);
1462
 
1463
		$this->_stmt = false;
1464
		$this->_connectionID = false;
1465
	}
1466
 
1467
	function MetaPrimaryKeys($table, $owner=false,$internalKey=false)
1468
	{
1469
		if ($internalKey) {
1470
			return array('ROWID');
1471
		}
1472
 
1473
		// tested with oracle 8.1.7
1474
		$table = strtoupper($table);
1475
		if ($owner) {
1476
			$owner_clause = "AND ((a.OWNER = b.OWNER) AND (a.OWNER = UPPER('$owner')))";
1477
			$ptab = 'ALL_';
1478
		} else {
1479
			$owner_clause = '';
1480
			$ptab = 'USER_';
1481
		}
1482
		$sql = "
1483
SELECT /*+ RULE */ distinct b.column_name
1484
   FROM {$ptab}CONSTRAINTS a
1485
	  , {$ptab}CONS_COLUMNS b
1486
  WHERE ( UPPER(b.table_name) = ('$table'))
1487
	AND (UPPER(a.table_name) = ('$table') and a.constraint_type = 'P')
1488
	$owner_clause
1489
	AND (a.constraint_name = b.constraint_name)";
1490
 
1491
		$rs = $this->Execute($sql);
1492
		if ($rs && !$rs->EOF) {
1493
			$arr = $rs->GetArray();
1494
			$a = array();
1495
			foreach($arr as $v) {
1496
				$a[] = reset($v);
1497
			}
1498
			return $a;
1499
		}
1500
		else return false;
1501
	}
1502
 
1503
	/**
1504
	 * Returns a list of Foreign Keys associated with a specific table.
1505
	 *
1506
	 * @param string $table
1507
	 * @param string $owner
1508
	 * @param bool   $upper       discarded
1509
	 * @param bool   $associative discarded
1510
	 *
1511
	 * @return string[]|false An array where keys are tables, and values are foreign keys;
1512
	 *                        false if no foreign keys could be found.
1513
	 */
1514
	public function metaForeignKeys($table, $owner = '', $upper = false, $associative = false)
1515
	{
1516
		global $ADODB_FETCH_MODE;
1517
 
1518
		$save = $ADODB_FETCH_MODE;
1519
		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
1520
		$table = $this->qstr(strtoupper($table));
1521
		if (!$owner) {
1522
			$owner = $this->user;
1523
			$tabp = 'user_';
1524
		} else
1525
			$tabp = 'all_';
1526
 
1527
		$owner = ' and owner='.$this->qstr(strtoupper($owner));
1528
 
1529
		$sql =
1530
"select constraint_name,r_owner,r_constraint_name
1531
	from {$tabp}constraints
1532
	where constraint_type = 'R' and table_name = $table $owner";
1533
 
1534
		$constraints = $this->GetArray($sql);
1535
		$arr = false;
1536
		foreach($constraints as $constr) {
1537
			$cons = $this->qstr($constr[0]);
1538
			$rowner = $this->qstr($constr[1]);
1539
			$rcons = $this->qstr($constr[2]);
1540
			$cols = $this->GetArray("select column_name from {$tabp}cons_columns where constraint_name=$cons $owner order by position");
1541
			$tabcol = $this->GetArray("select table_name,column_name from {$tabp}cons_columns where owner=$rowner and constraint_name=$rcons order by position");
1542
 
1543
			if ($cols && $tabcol)
1544
				for ($i=0, $max=sizeof($cols); $i < $max; $i++) {
1545
					$arr[$tabcol[$i][0]] = $cols[$i][0].'='.$tabcol[$i][1];
1546
				}
1547
		}
1548
		$ADODB_FETCH_MODE = $save;
1549
 
1550
		return $arr;
1551
	}
1552
 
1553
 
1554
	function CharMax()
1555
	{
1556
		return 4000;
1557
	}
1558
 
1559
	function TextMax()
1560
	{
1561
		return 4000;
1562
	}
1563
 
1564
	/**
1565
	 * Correctly quotes a string so that all strings are escaped.
1566
	 * We prefix and append to the string single-quotes.
1567
	 * An example is  $db->qstr("Don't bother");
1568
	 *
1569
	 * @param string $s            The string to quote
1570
	 * @param bool   $magic_quotes This param is not used since 5.21.0.
1571
	 *                             It remains for backwards compatibility.
1572
	 *
1573
	 * @return string Quoted string to be sent back to database
1574
	 *
1575
	 * @noinspection PhpUnusedParameterInspection
1576
	 */
1577
	function qStr($s, $magic_quotes=false)
1578
	{
1579
		if ($this->noNullStrings && strlen($s) == 0) {
1580
			$s = ' ';
1581
		}
1582
		else if (strlen($s) == 0) {
1583
			return "''";
1584
		}
1585
		if ($this->replaceQuote[0] == '\\'){
1586
			$s = str_replace('\\','\\\\',$s);
1587
		}
1588
		return  "'" . str_replace("'", $this->replaceQuote, $s) . "'";
1589
	}
1590
 
1591
}
1592
 
1593
/*--------------------------------------------------------------------------------------
1594
	Class Name: Recordset
1595
--------------------------------------------------------------------------------------*/
1596
 
1597
class ADORecordset_oci8 extends ADORecordSet {
1598
 
1599
	var $databaseType = 'oci8';
1600
	var $bind=false;
1601
	var $_fieldobjs;
1602
 
1603
	/** @var resource Cursor reference */
1604
	var $_refcursor;
1605
 
1606
	function __construct($queryID,$mode=false)
1607
	{
1608
		if ($mode === false) {
1609
			global $ADODB_FETCH_MODE;
1610
			$mode = $ADODB_FETCH_MODE;
1611
		}
1612
		switch ($mode) {
1613
			case ADODB_FETCH_ASSOC:
1614
				$this->fetchMode = OCI_ASSOC;
1615
				break;
1616
			case ADODB_FETCH_DEFAULT:
1617
			case ADODB_FETCH_BOTH:
1618
				$this->fetchMode = OCI_NUM + OCI_ASSOC;
1619
				break;
1620
			case ADODB_FETCH_NUM:
1621
			default:
1622
				$this->fetchMode = OCI_NUM;
1623
				break;
1624
		}
1625
		$this->fetchMode += OCI_RETURN_NULLS + OCI_RETURN_LOBS;
1626
		$this->adodbFetchMode = $mode;
1627
		$this->_queryID = $queryID;
1628
	}
1629
 
1630
	/**
1631
	* Overrides the core destructor method as that causes problems here
1632
	*
1633
	* @return void
1634
	*/
1635
	function __destruct() {}
1636
 
1637
	function Init()
1638
	{
1639
		if ($this->_inited) {
1640
			return;
1641
		}
1642
 
1643
		$this->_inited = true;
1644
		if ($this->_queryID) {
1645
 
1646
			$this->_currentRow = 0;
1647
			@$this->_initrs();
1648
			if ($this->_numOfFields) {
1649
				$this->EOF = !$this->_fetch();
1650
			}
1651
			else $this->EOF = true;
1652
 
1653
			/*
1654
			// based on idea by Gaetano Giunta to detect unusual oracle errors
1655
			// see PHPLens Issue No: 6771
1656
			$err = oci_error($this->_queryID);
1657
			if ($err && $this->connection->debug) {
1658
				ADOConnection::outp($err);
1659
			}
1660
			*/
1661
 
1662
			if (!is_array($this->fields)) {
1663
				$this->_numOfRows = 0;
1664
				$this->fields = array();
1665
			}
1666
		} else {
1667
			$this->fields = array();
1668
			$this->_numOfRows = 0;
1669
			$this->_numOfFields = 0;
1670
			$this->EOF = true;
1671
		}
1672
	}
1673
 
1674
	function _initrs()
1675
	{
1676
		$this->_numOfRows = -1;
1677
		$this->_numOfFields = oci_num_fields($this->_queryID);
1678
		if ($this->_numOfFields>0) {
1679
			$this->_fieldobjs = array();
1680
			$max = $this->_numOfFields;
1681
			for ($i=0;$i<$max; $i++) $this->_fieldobjs[] = $this->_FetchField($i);
1682
		}
1683
	}
1684
 
1685
	/**
1686
	 * Get column information in the Recordset object.
1687
	 * fetchField() can be used in order to obtain information about fields
1688
	 * in a certain query result. If the field offset isn't specified, the next
1689
	 * field that wasn't yet retrieved by fetchField() is retrieved
1690
	 *
1691
	 * @return object containing field information
1692
	 */
1693
	function _FetchField($fieldOffset = -1)
1694
	{
1695
		$fld = new ADOFieldObject;
1696
		$fieldOffset += 1;
1697
		$fld->name =oci_field_name($this->_queryID, $fieldOffset);
1698
		if (ADODB_ASSOC_CASE == ADODB_ASSOC_CASE_LOWER) {
1699
			$fld->name = strtolower($fld->name);
1700
		}
1701
		$fld->type = oci_field_type($this->_queryID, $fieldOffset);
1702
		$fld->max_length = oci_field_size($this->_queryID, $fieldOffset);
1703
 
1704
		switch($fld->type) {
1705
			case 'NUMBER':
1706
				$p = oci_field_precision($this->_queryID, $fieldOffset);
1707
				$sc = oci_field_scale($this->_queryID, $fieldOffset);
1708
				if ($p != 0 && $sc == 0) {
1709
					$fld->type = 'INT';
1710
				}
1711
				$fld->scale = $p;
1712
				break;
1713
 
1714
			case 'CLOB':
1715
			case 'NCLOB':
1716
			case 'BLOB':
1717
				$fld->max_length = -1;
1718
				break;
1719
		}
1720
		return $fld;
1721
	}
1722
 
1723
	/* For some reason, oci_field_name fails when called after _initrs() so we cache it */
1724
	function FetchField($fieldOffset = -1)
1725
	{
1726
		return $this->_fieldobjs[$fieldOffset];
1727
	}
1728
 
1729
 
1730
	function MoveNext()
1731
	{
1732
		if ($this->fields = @oci_fetch_array($this->_queryID,$this->fetchMode)) {
1733
			$this->_currentRow += 1;
1734
			$this->_updatefields();
1735
			return true;
1736
		}
1737
		if (!$this->EOF) {
1738
			$this->_currentRow += 1;
1739
			$this->EOF = true;
1740
		}
1741
		return false;
1742
	}
1743
 
1744
	// Optimize SelectLimit() by using oci_fetch()
1745
	function GetArrayLimit($nrows,$offset=-1)
1746
	{
1747
		if ($offset <= 0) {
1748
			$arr = $this->GetArray($nrows);
1749
			return $arr;
1750
		}
1751
		$arr = array();
1752
		for ($i=1; $i < $offset; $i++) {
1753
			if (!@oci_fetch($this->_queryID)) {
1754
				return $arr;
1755
			}
1756
		}
1757
 
1758
		if (!$this->fields = @oci_fetch_array($this->_queryID,$this->fetchMode)) {
1759
			return $arr;
1760
		}
1761
		$this->_updatefields();
1762
		$results = array();
1763
		$cnt = 0;
1764
		while (!$this->EOF && $nrows != $cnt) {
1765
			$results[$cnt++] = $this->fields;
1766
			$this->MoveNext();
1767
		}
1768
 
1769
		return $results;
1770
	}
1771
 
1772
 
1773
	// Use associative array to get fields array
1774
	function Fields($colname)
1775
	{
1776
		if (!$this->bind) {
1777
			$this->bind = array();
1778
			for ($i=0; $i < $this->_numOfFields; $i++) {
1779
				$o = $this->FetchField($i);
1780
				$this->bind[strtoupper($o->name)] = $i;
1781
			}
1782
		}
1783
 
1784
		return $this->fields[$this->bind[strtoupper($colname)]];
1785
	}
1786
 
1787
 
1788
	function _seek($row)
1789
	{
1790
		return false;
1791
	}
1792
 
1793
	function _fetch()
1794
	{
1795
		$this->fields = @oci_fetch_array($this->_queryID,$this->fetchMode);
1796
		$this->_updatefields();
1797
 
1798
		return $this->fields;
1799
	}
1800
 
1801
	/**
1802
	 * close() only needs to be called if you are worried about using too much
1803
	 * memory while your script is running. All associated result memory for the
1804
	 * specified result identifier will automatically be freed.
1805
	 */
1806
	function _close()
1807
	{
1808
		if ($this->connection->_stmt === $this->_queryID) {
1809
			$this->connection->_stmt = false;
1810
		}
1811
		if (!empty($this->_refcursor)) {
1812
			oci_free_cursor($this->_refcursor);
1813
			$this->_refcursor = false;
1814
		}
1815
		if (is_resource($this->_queryID))
1816
		   @oci_free_statement($this->_queryID);
1817
		$this->_queryID = false;
1818
	}
1819
 
1820
	/**
1821
	 * not the fastest implementation - quick and dirty - jlim
1822
	 * for best performance, use the actual $rs->MetaType().
1823
	 *
1824
	 * @param	mixed	$t
1825
	 * @param	int		$len		[optional] Length of blobsize
1826
	 * @param	bool	$fieldobj	[optional][discarded]
1827
	 * @return	str					The metatype of the field
1828
	 */
1829
	function MetaType($t, $len=-1, $fieldobj=false)
1830
	{
1831
		if (is_object($t)) {
1832
			$fieldobj = $t;
1833
			$t = $fieldobj->type;
1834
			$len = $fieldobj->max_length;
1835
		}
1836
 
1837
		$t = strtoupper($t);
1838
 
1839
		if (array_key_exists($t,$this->connection->customActualTypes))
1840
			return  $this->connection->customActualTypes[$t];
1841
 
1842
		switch ($t) {
1843
		case 'VARCHAR':
1844
		case 'VARCHAR2':
1845
		case 'CHAR':
1846
		case 'VARBINARY':
1847
		case 'BINARY':
1848
		case 'NCHAR':
1849
		case 'NVARCHAR':
1850
		case 'NVARCHAR2':
1851
			if ($len <= $this->blobSize) {
1852
				return 'C';
1853
			}
1854
 
1855
		case 'NCLOB':
1856
		case 'LONG':
1857
		case 'LONG VARCHAR':
1858
		case 'CLOB':
1859
		return 'X';
1860
 
1861
		case 'LONG RAW':
1862
		case 'LONG VARBINARY':
1863
		case 'BLOB':
1864
			return 'B';
1865
 
1866
		case 'DATE':
1867
			return  ($this->connection->datetime) ? 'T' : 'D';
1868
 
1869
 
1870
		case 'TIMESTAMP': return 'T';
1871
 
1872
		case 'INT':
1873
		case 'SMALLINT':
1874
		case 'INTEGER':
1875
			return 'I';
1876
 
1877
		default:
1878
			return ADODB_DEFAULT_METATYPE;
1879
		}
1880
	}
1881
}
1882
 
1883
class ADORecordSet_ext_oci8 extends ADORecordSet_oci8 {
1884
 
1885
	function MoveNext()
1886
	{
1887
		return adodb_movenext($this);
1888
	}
1889
}