Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
/**
3
 * IBM DB2 Native Client driver.
4
 *
5
 * Originally DB2 drivers were dependent on an ODBC driver, and some installations
6
 * may still use that. To use an ODBC driver connection, use the odbc_db2
7
 * ADOdb driver. For Linux, you need the 'ibm_db2' PECL extension for PHP,
8
 * For Windows, you need to locate an appropriate version of the php_ibm_db2.dll,
9
 * as well as the IBM data server client software.
10
 * This is basically a full rewrite of the original driver, for information
11
 * about all the changes, see the update information on the ADOdb website
12
 * for version 5.21.0.
13
 *
14
 * @link http://pecl.php.net/package/ibm_db2 PECL Extension For DB2
15
 *
16
 * This file is part of ADOdb, a Database Abstraction Layer library for PHP.
17
 *
18
 * @package ADOdb
19
 * @link https://adodb.org Project's web site and documentation
20
 * @link https://github.com/ADOdb/ADOdb Source code and issue tracker
21
 *
22
 * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause
23
 * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option,
24
 * any later version. This means you can use it in proprietary products.
25
 * See the LICENSE.md file distributed with this source code for details.
26
 * @license BSD-3-Clause
27
 * @license LGPL-2.1-or-later
28
 *
29
 * @copyright 2000-2013 John Lim
30
 * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community
31
 * @author Mark Newnham
32
 */
33
 
34
// security - hide paths
35
if (!defined('ADODB_DIR')) die();
36
 
37
define("_ADODB_DB2_LAYER", 2 );
38
 
39
 
40
class ADODB_db2 extends ADOConnection {
41
	var $databaseType = "db2";
42
	var $fmtDate = "'Y-m-d'";
43
	var $concat_operator = '||';
44
 
45
	var $sysTime = 'CURRENT TIME';
46
	var $sysDate = 'CURRENT DATE';
47
	var $sysTimeStamp = 'CURRENT TIMESTAMP';
48
 
49
	var $fmtTimeStamp = "'Y-m-d H:i:s'";
50
	var $replaceQuote = "''"; // string to use to replace quotes
51
	var $dataProvider = "db2";
52
	var $hasAffectedRows = true;
53
 
54
	var $binmode = DB2_BINARY;
55
 
56
	/*
57
	* setting this to true will make array elements in FETCH_ASSOC
58
	* mode case-sensitive breaking backward-compat
59
	*/
60
	var $useFetchArray = false;
61
	var $_bindInputArray = true;
62
	var $_genIDSQL = "VALUES NEXTVAL FOR %s";
63
	var $_genSeqSQL = "
64
	CREATE SEQUENCE %s START WITH %s
65
	NO MAXVALUE NO CYCLE INCREMENT BY 1 NO CACHE
66
	";
67
	var $_dropSeqSQL = "DROP SEQUENCE %s";
68
	var $_autocommit = true;
69
	var $_lastAffectedRows = 0;
70
	var $hasInsertID = true;
71
	var $hasGenID    = true;
72
 
73
	/*
74
	 * Character used to wrap column and table names for escaping special
75
	 * characters in column and table names as well as forcing upper and
76
	 * lower case
77
	 */
78
	public $nameQuote = '"';
79
 
80
	/*
81
	 * Holds information about the stored procedure request
82
	 * currently being built
83
	 */
84
	private $storedProcedureParameters = false;
85
 
86
 
87
	function __construct() {}
88
 
89
	protected function _insertID($table = '', $column = '')
90
	{
91
		return ADOConnection::GetOne('VALUES IDENTITY_VAL_LOCAL()');
92
	}
93
 
94
	public function _connect($argDSN, $argUsername, $argPassword, $argDatabasename)
95
	{
96
		return $this->doDB2Connect($argDSN, $argUsername, $argPassword, $argDatabasename);
97
	}
98
 
99
	public function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename)
100
	{
101
		return $this->doDB2Connect($argDSN, $argUsername, $argPassword, $argDatabasename,true);
102
	}
103
 
104
	private function doDB2Connect($argDSN, $argUsername, $argPassword, $argDatabasename, $persistent=false)
105
	{
106
 
107
		if (!function_exists('db2_connect')) {
108
			ADOConnection::outp("DB2 extension not installed.");
109
			return null;
110
		}
111
 
112
		$connectionParameters = $this->unpackParameters($argDSN,
113
														$argUsername,
114
														$argPassword,
115
														$argDatabasename);
116
 
117
		if ($connectionParameters == null)
118
		{
119
			/*
120
			 * Error thrown
121
			 */
122
			return null;
123
		}
124
 
125
		$argDSN 		         = $connectionParameters['dsn'];
126
		$argUsername 	         = $connectionParameters['uid'];
127
		$argPassword 	         = $connectionParameters['pwd'];
128
		$argDatabasename         = $connectionParameters['database'];
129
		$useCataloguedConnection = $connectionParameters['catalogue'];
130
 
131
		if ($this->debug){
132
			if ($useCataloguedConnection){
133
				$connectMessage = "Catalogued connection using parameters: ";
134
				$connectMessage .= "DB=$argDatabasename / ";
135
				$connectMessage .= "UID=$argUsername / ";
136
				$connectMessage .= "PWD=$argPassword";
137
			}
138
			else
139
			{
140
				$connectMessage = "Uncatalogued connection using DSN: $argDSN";
141
			}
142
			ADOConnection::outp($connectMessage);
143
		}
144
		/*
145
		 * This needs to be set before the connect().
146
		 */
147
		ini_set('ibm_db2.binmode', $this->binmode);
148
 
149
		if ($persistent)
150
			$db2Function = 'db2_pconnect';
151
		else
152
			$db2Function = 'db2_connect';
153
 
154
		/*
155
		* We need to flatten out the connectionParameters
156
		*/
157
 
158
		$db2Options = array();
159
		if ($this->connectionParameters)
160
		{
161
			foreach($this->connectionParameters as $p)
162
				foreach($p as $k=>$v)
163
					$db2Options[$k] = $v;
164
		}
165
 
166
		if ($useCataloguedConnection)
167
			$this->_connectionID = $db2Function($argDatabasename,
168
												$argUsername,
169
												$argPassword,
170
												$db2Options);
171
		else
172
			$this->_connectionID = $db2Function($argDSN,
173
												null,
174
												null,
175
												$db2Options);
176
 
177
 
178
		$this->_errorMsg = @db2_conn_errormsg();
179
 
180
		if ($this->_connectionID && $this->connectStmt)
181
			$this->execute($this->connectStmt);
182
 
183
		return $this->_connectionID != false;
184
 
185
	}
186
 
187
	/**
188
	 * Validates and preprocesses the passed parameters for consistency
189
	 *
190
	 * @param	string	$argDSN				Either DSN or database
191
	 * @param	string	$argUsername		User name or null
192
	 * @param	string	$argPassword		Password or null
193
	 * @param	string	$argDatabasename	Either DSN or database
194
	 *
195
	 * @return mixed  array if correct, null if not
196
	 */
197
	private function unpackParameters($argDSN, $argUsername, $argPassword, $argDatabasename)
198
	{
199
 
200
 
201
		$connectionParameters = array('dsn'=>'',
202
									  'uid'=>'',
203
									  'pwd'=>'',
204
									  'database'=>'',
205
									  'catalogue'=>true
206
									  );
207
 
208
		/*
209
		 * Uou can either connect to a catalogued connection
210
		 * with a database name e.g. 'SAMPLE'
211
		 * or an uncatalogued connection with a DSN like connection
212
		 * DATABASE=database;HOSTNAME=hostname;PORT=port;PROTOCOL=TCPIP;UID=username;PWD=password;
213
		 */
214
 
215
		if (!$argDSN && !$argDatabasename)
216
		{
217
			$errorMessage = 'Supply either catalogued or uncatalogued connection parameters';
218
			$this->_errorMsg = $errorMessage;
219
			if ($this->debug)
220
				ADOConnection::outp($errorMessage);
221
			return null;
222
		}
223
 
224
		$useCataloguedConnection = true;
225
		$schemaName 			 = '';
226
 
227
		if ($argDSN && $argDatabasename)
228
		{
229
			/*
230
			 * If a catalogued connection if provided,
231
			 * as well as user and password
232
			 * that will take priority
233
			 */
234
			if ($argUsername && $argPassword && !$this->isDsn($argDatabasename))
235
			{
236
				if ($this->debug){
237
					$errorMessage = 'Warning: Because you provided user,';
238
					$errorMessage.= 'password and database, DSN connection ';
239
					$errorMessage.= 'parameters were discarded';
240
					ADOConnection::outp($errorMessage);
241
 
242
				}
243
				$argDSN = '';
244
			}
245
			else if ($this->isDsn($argDSN) && $this->isDsn($argDatabasename))
246
			{
247
				$errorMessage = 'Supply uncatalogued connection parameters ';
248
				$errorMessage.= 'in either the database or DSN arguments, ';
249
				$errorMessage.= 'but not both';
250
 
251
				if ($this->debug)
252
					ADOConnection::outp($errorMessage);
253
				return null;
254
			}
255
		}
256
 
257
		if (!$this->isDsn($argDSN) && $this->isDsn($argDatabasename))
258
		{
259
			/*
260
			 * Switch them around for next test
261
			 */
262
			$temp           = $argDSN;
263
			$argDsn         = $argDatabasename;
264
			$argDatabasenME = $temp;
265
		}
266
 
267
		if ($this->isDsn($argDSN))
268
		{
269
 
270
			if (!preg_match('/uid=/i',$argDSN)
271
			||  !preg_match('/pwd=/i',$argDSN))
272
			{
273
				$errorMessage = 'For uncatalogued connections, provide ';
274
				$errorMessage.= 'both UID and PWD in the connection string';
275
 
276
				if ($this->debug)
277
					ADOConnection::outp($errorMessage);
278
				return null;
279
			}
280
 
281
			if (preg_match('/database=/i',$argDSN))
282
			{
283
				if ($argDatabasename)
284
				{
285
					$argDatabasename = '';
286
					if ($this->debug)
287
					{
288
						$errorMessage = 'Warning: Because you provided ';
289
						$errorMessage.= 'database information in the DSN ';
290
						$errorMessage.= 'parameters, the supplied database ';
291
						$errorMessage.= 'name was discarded';
292
						ADOConnection::outp($errorMessage);
293
					}
294
				}
295
				$useCataloguedConnection = false;
296
 
297
			}
298
			elseif ($argDatabasename)
299
			{
300
				$this->database = $argDatabasename;
301
				$argDSN .= ';database=' . $argDatabasename;
302
				$argDatabasename = '';
303
				$useCataloguedConnection = false;
304
 
305
			}
306
			else
307
			{
308
				$errorMessage = 'Uncatalogued connection parameters ';
309
				$errorMessage.= 'must contain a database= argument';
310
 
311
				if ($this->debug)
312
					ADOConnection::outp($errorMessage);
313
				return null;
314
			}
315
		}
316
 
317
		if ($argDSN && !$argDatabasename && $useCataloguedConnection)
318
		{
319
			$argDatabasename = $argDSN;
320
			$argDSN          = '';
321
		}
322
 
323
 
324
		if ($useCataloguedConnection
325
		&& (!$argDatabasename
326
		|| !$argUsername
327
		|| !$argPassword))
328
		{
329
 
330
			$errorMessage = 'For catalogued connections, provide ';
331
			$errorMessage.= 'database, username and password';
332
			$this->_errorMsg = $errorMessage;
333
			if ($this->debug)
334
				ADOConnection::outp($errorMessage);
335
			return null;
336
 
337
		}
338
 
339
		if ($argDatabasename)
340
			$this->database = $argDatabasename;
341
		elseif (!$this->database)
342
			$this->database = $this->getDatabasenameFromDsn($argDSN);
343
 
344
 
345
		$connectionParameters = array('dsn'=>$argDSN,
346
									  'uid'=>$argUsername,
347
									  'pwd'=>$argPassword,
348
									  'database'=>$argDatabasename,
349
									  'catalogue'=>$useCataloguedConnection
350
									  );
351
 
352
		return $connectionParameters;
353
 
354
	}
355
 
356
	/**
357
	  * Does the provided string look like a DSN
358
	  *
359
	  * @param	string	$dsnString
360
	  *
361
	  * @return bool
362
	  */
363
	private function isDsn($dsnString){
364
		$dsnArray = preg_split('/[;=]+/',$dsnString);
365
		if (count($dsnArray) > 2)
366
			return true;
367
		return false;
368
	}
369
 
370
 
371
	/**
372
	  * Gets the database name from the DSN
373
	  *
374
	  * @param	string	$dsnString
375
	  *
376
	  * @return string
377
	  */
378
	private function getDatabasenameFromDsn($dsnString){
379
 
380
		$dsnArray = preg_split('/[;=]+/',$dsnString);
381
		$dbIndex  = array_search('database',$dsnArray);
382
 
383
		return $dsnArray[$dbIndex + 1];
384
	}
385
 
386
 
387
	/**
388
	* format and return date string in database timestamp format
389
	*
390
	* @param	mixed	$ts		either a string or a unixtime
391
	* @param	bool	$isField	discarded
392
	*
393
	* @return string
394
	*/
395
	function dbTimeStamp($ts,$isField=false)
396
	{
397
		if (empty($ts) && $ts !== 0) return 'null';
398
		if (is_string($ts)) $ts = ADORecordSet::unixTimeStamp($ts);
399
		return 'TO_DATE('.adodb_date($this->fmtTimeStamp,$ts).",'YYYY-MM-DD HH24:MI:SS')";
400
	}
401
 
402
	/**
403
	* Format date column in sql string given an input format that understands Y M D
404
	*
405
	* @param	string	$fmt
406
	* @param	bool	$col
407
	*
408
	* @return string
409
	*/
410
	function sqlDate($fmt, $col=false)
411
	{
412
		if (!$col) $col = $this->sysDate;
413
 
414
		/* use TO_CHAR() if $fmt is TO_CHAR() allowed fmt */
415
		if ($fmt== 'Y-m-d H:i:s')
416
			return 'TO_CHAR('.$col.", 'YYYY-MM-DD HH24:MI:SS')";
417
 
418
		$s = '';
419
 
420
		$len = strlen($fmt);
421
		for ($i=0; $i < $len; $i++) {
422
			if ($s) $s .= $this->concat_operator;
423
			$ch = $fmt[$i];
424
			switch($ch) {
425
			case 'Y':
426
			case 'y':
427
				if ($len==1) return "year($col)";
428
				$s .= "char(year($col))";
429
				break;
430
			case 'M':
431
				if ($len==1) return "monthname($col)";
432
				$s .= "substr(monthname($col),1,3)";
433
				break;
434
			case 'm':
435
				if ($len==1) return "month($col)";
436
				$s .= "right(digits(month($col)),2)";
437
				break;
438
			case 'D':
439
			case 'd':
440
				if ($len==1) return "day($col)";
441
				$s .= "right(digits(day($col)),2)";
442
				break;
443
			case 'H':
444
			case 'h':
445
				if ($len==1) return "hour($col)";
446
				if ($col != $this->sysDate) $s .= "right(digits(hour($col)),2)";
447
				else $s .= "''";
448
				break;
449
			case 'i':
450
			case 'I':
451
				if ($len==1) return "minute($col)";
452
				if ($col != $this->sysDate)
453
					$s .= "right(digits(minute($col)),2)";
454
					else $s .= "''";
455
				break;
456
			case 'S':
457
			case 's':
458
				if ($len==1) return "second($col)";
459
				if ($col != $this->sysDate)
460
					$s .= "right(digits(second($col)),2)";
461
				else $s .= "''";
462
				break;
463
			default:
464
				if ($ch == '\\') {
465
					$i++;
466
					$ch = substr($fmt,$i,1);
467
				}
468
				$s .= $this->qstr($ch);
469
			}
470
		}
471
		return $s;
472
	}
473
 
474
 
475
	function serverInfo()
476
	{
477
		$sql = "SELECT service_level, fixpack_num
478
				  FROM TABLE(sysproc.env_get_inst_info())
479
					AS INSTANCEINFO";
480
		$row = $this->GetRow($sql);
481
 
482
 
483
		if ($row) {
484
			$info['version'] = $row[0].':'.$row[1];
485
			$info['fixpack'] = $row[1];
486
			$info['description'] = '';
487
		} else {
488
			return ADOConnection::serverInfo();
489
		}
490
 
491
		return $info;
492
	}
493
 
494
	function createSequence($seqname='adodbseq',$start=1)
495
	{
496
		if (empty($this->_genSeqSQL))
497
			return false;
498
 
499
		$ok = $this->execute(sprintf($this->_genSeqSQL,$seqname,$start));
500
		if (!$ok)
501
			return false;
502
		return true;
503
	}
504
 
505
	function dropSequence($seqname='adodbseq')
506
	{
507
		if (empty($this->_dropSeqSQL)) return false;
508
		return $this->execute(sprintf($this->_dropSeqSQL,$seqname));
509
	}
510
 
511
	function selectLimit($sql,$nrows=-1,$offset=-1,$inputArr=false,$secs2cache=0)
512
	{
513
		$nrows = (integer) $nrows;
514
 
515
		if ($offset <= 0)
516
		{
517
			if ($nrows >= 0)
518
				$sql .=  " FETCH FIRST $nrows ROWS ONLY ";
519
 
520
			$rs = $this->execute($sql,$inputArr);
521
 
522
		}
523
		else
524
		{
525
			if ($offset > 0 && $nrows < 0);
526
 
527
			else
528
			{
529
				$nrows += $offset;
530
				$sql .=  " FETCH FIRST $nrows ROWS ONLY ";
531
			}
532
 
533
			/*
534
			 * DB2 has no native support for mid table offset
535
			 */
536
			$rs = ADOConnection::selectLimit($sql,$nrows,$offset,$inputArr);
537
 
538
		}
539
 
540
		return $rs;
541
	}
542
 
543
 
544
	function errorMsg()
545
	{
546
		if ($this->_errorMsg !== false)
547
			return $this->_errorMsg;
548
 
549
		if (empty($this->_connectionID))
550
			return @db2_conn_errormsg();
551
 
552
		return @db2_conn_errormsg($this->_connectionID);
553
	}
554
 
555
	function errorNo()
556
	{
557
 
558
		if ($this->_errorCode !== false)
559
			return $this->_errorCode;
560
 
561
 
562
		if (empty($this->_connectionID))
563
			$e = @db2_conn_error();
564
 
565
		else
566
			$e = @db2_conn_error($this->_connectionID);
567
 
568
		return $e;
569
	}
570
 
571
 
572
 
573
	function beginTrans()
574
	{
575
		if (!$this->hasTransactions)
576
			return false;
577
		if ($this->transOff)
578
			return true;
579
 
580
		$this->transCnt += 1;
581
 
582
		$this->_autocommit = false;
583
 
584
		return db2_autocommit($this->_connectionID,false);
585
	}
586
 
587
	function CommitTrans($ok=true)
588
	{
589
		if ($this->transOff)
590
			return true;
591
 
592
		if (!$ok)
593
			return $this->RollbackTrans();
594
 
595
		if ($this->transCnt)
596
			$this->transCnt -= 1;
597
 
598
		$this->_autocommit = true;
599
		$ret = @db2_commit($this->_connectionID);
600
		@db2_autocommit($this->_connectionID,true);
601
		return $ret;
602
	}
603
 
604
	function RollbackTrans()
605
	{
606
		if ($this->transOff) return true;
607
		if ($this->transCnt) $this->transCnt -= 1;
608
		$this->_autocommit = true;
609
		$ret = @db2_rollback($this->_connectionID);
610
		@db2_autocommit($this->_connectionID,true);
611
		return $ret;
612
	}
613
 
614
	/**
615
	 * Return a list of Primary Keys for a specified table
616
	 *
617
	 * We don't use db2_statistics as the function does not seem to play
618
	 * well with mixed case table names
619
	 *
620
	 * @param string   $table
621
	 * @param bool     $primary    (optional) only return primary keys
622
	 * @param bool     $owner      (optional) not used in this driver
623
	 *
624
	 * @return string[]    Array of indexes
625
	 */
626
	public function metaPrimaryKeys($table,$owner=false)
627
	{
628
 
629
		$primaryKeys = array();
630
 
631
		global $ADODB_FETCH_MODE;
632
 
633
		$schema = '';
634
		$this->_findschema($table,$schema);
635
 
636
		$table = $this->getTableCasedValue($table);
637
 
638
		$savem 			  = $ADODB_FETCH_MODE;
639
		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
640
		$this->setFetchMode(ADODB_FETCH_NUM);
641
 
642
 
643
		$sql = "SELECT *
644
				  FROM syscat.indexes
645
				 WHERE tabname='$table'";
646
 
647
		$rows = $this->getAll($sql);
648
 
649
		$this->setFetchMode($savem);
650
		$ADODB_FETCH_MODE = $savem;
651
 
652
		if (empty($rows))
653
			return false;
654
 
655
		foreach ($rows as $r)
656
		{
657
			if ($r[7] != 'P')
658
				continue;
659
 
660
			$cols = explode('+',$r[6]);
661
			foreach ($cols as $colIndex=>$col)
662
			{
663
				if ($colIndex == 0)
664
					continue;
665
				$columnName = $this->getMetaCasedValue($col);
666
				$primaryKeys[] = $columnName;
667
			}
668
			break;
669
		}
670
		return $primaryKeys;
671
	}
672
 
673
	/**
674
	 * Returns a list of Foreign Keys associated with a specific table.
675
	 *
676
	 * @param string $table
677
	 * @param string $owner       discarded
678
	 * @param bool   $upper       discarded
679
	 * @param bool   $associative discarded
680
	 *
681
	 * @return string[]|false An array where keys are tables, and values are foreign keys;
682
	 *                        false if no foreign keys could be found.
683
	 */
684
	public function metaForeignKeys($table, $owner = '', $upper = false, $associative = false)
685
	{
686
 
687
		global $ADODB_FETCH_MODE;
688
 
689
		$schema = '';
690
		$this->_findschema($table,$schema);
691
 
692
		$savem = $ADODB_FETCH_MODE;
693
		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
694
 
695
		$this->setFetchMode(ADODB_FETCH_NUM);
696
 
697
		$sql = "SELECT SUBSTR(tabname,1,20) table_name,
698
					   SUBSTR(constname,1,20) fk_name,
699
					   SUBSTR(REFTABNAME,1,12) parent_table,
700
					   SUBSTR(refkeyname,1,20) pk_orig_table,
701
					   fk_colnames
702
				 FROM syscat.references
703
				WHERE tabname = '$table'";
704
 
705
		$results = $this->getAll($sql);
706
 
707
		$ADODB_FETCH_MODE = $savem;
708
		$this->setFetchMode($savem);
709
 
710
		if (empty($results))
711
			return false;
712
 
713
		$foreignKeys = array();
714
 
715
		foreach ($results as $r)
716
		{
717
			$parentTable = trim($this->getMetaCasedValue($r[2]));
718
			$keyName     = trim($this->getMetaCasedValue($r[1]));
719
			$foreignKeys[$parentTable] = $keyName;
720
		}
721
 
722
		return $foreignKeys;
723
	}
724
 
725
	/**
726
	 * Returns a list of tables
727
	 *
728
	 * @param string	$ttype (optional)
729
	 * @param	string	$schema	(optional)
730
	 * @param	string	$mask	(optional)
731
	 *
732
	 * @return array
733
	 */
734
	public function metaTables($ttype=false,$schema=false,$mask=false)
735
	{
736
 
737
		global $ADODB_FETCH_MODE;
738
 
739
		$savem 			  = $ADODB_FETCH_MODE;
740
		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
741
 
742
		/*
743
		* Values for TABLE_TYPE
744
		* ---------------------------
745
		* ALIAS, HIERARCHY TABLE, INOPERATIVE VIEW, NICKNAME,
746
		* MATERIALIZED QUERY TABLE, SYSTEM TABLE, TABLE,
747
		* TYPED TABLE, TYPED VIEW, and VIEW
748
		*
749
		* If $ttype passed as '', match 'TABLE' and 'VIEW'
750
		* If $ttype passed as 'T' it is assumed to be 'TABLE'
751
		* if $ttype passed as 'V' it is assumed to be 'VIEW'
752
		*/
753
		$ttype = strtoupper($ttype);
754
		if ($ttype) {
755
			/*
756
			 * @todo We could do valid type checking or array type
757
			 */
758
			 if ($ttype == 'V')
759
				$ttype = 'VIEW';
760
			if ($ttype == 'T')
761
				$ttype = 'TABLE';
762
		}
763
 
764
		if (!$schema)
765
			$schema = '%';
766
 
767
		if (!$mask)
768
			$mask = '%';
769
 
770
		$qid = @db2_tables($this->_connectionID,NULL,$schema,$mask,$ttype);
771
 
772
		$rs = new ADORecordSet_db2($qid);
773
 
774
		$ADODB_FETCH_MODE = $savem;
775
 
776
		if (!$rs)
777
			return false;
778
 
779
		$arr = $rs->getArray();
780
 
781
		$rs->Close();
782
 
783
		$tableList = array();
784
 
785
		/*
786
		* Array items
787
		* ---------------------------------
788
		* 0 TABLE_CAT	The catalog that contains the table.
789
		*				The value is NULL if this table does not have catalogs.
790
		* 1 TABLE_SCHEM	Name of the schema that contains the table.
791
		* 2 TABLE_NAME	Name of the table.
792
		* 3 TABLE_TYPE	Table type identifier for the table.
793
		* 4 REMARKS		Description of the table.
794
		*/
795
 
796
		for ($i=0; $i < sizeof($arr); $i++)
797
		{
798
 
799
			$tableRow = $arr[$i];
800
			$tableName = $tableRow[2];
801
			$tableType = $tableRow[3];
802
 
803
			if (!$tableName)
804
				continue;
805
 
806
			if ($ttype == '' && (strcmp($tableType,'TABLE') <> 0 && strcmp($tableType,'VIEW') <> 0))
807
				continue;
808
 
809
			/*
810
			 * Set metacasing if required
811
			 */
812
			$tableName = $this->getMetaCasedValue($tableName);
813
 
814
			/*
815
			 * If we requested a schema, we prepend the schema
816
			   name to the table name
817
			 */
818
			if (strcmp($schema,'%') <> 0)
819
				$tableName = $schema . '.' . $tableName;
820
 
821
			$tableList[] = $tableName;
822
 
823
		}
824
		return $tableList;
825
	}
826
 
827
	/**
828
	  * Return a list of indexes for a specified table
829
	  *
830
	  * We don't use db2_statistics as the function does not seem to play
831
	  * well with mixed case table names
832
	  *
833
	  * @param string   $table
834
	  * @param bool     $primary    (optional) only return primary keys
835
	  * @param bool     $owner      (optional) not used in this driver
836
	  *
837
	  * @return string[]    Array of indexes
838
	  */
839
	public function metaIndexes($table, $primary = false, $owner = false) {
840
 
841
		global $ADODB_FETCH_MODE;
842
 
843
		 /* Array(
844
		 *   [name_of_index] => Array(
845
		 *     [unique] => true or false
846
		 *     [columns] => Array(
847
		 *       [0] => firstcol
848
		 *       [1] => nextcol
849
		 *       [2] => etc........
850
		 *     )
851
		 *   )
852
		 * )
853
		 */
854
		$indices 		= array();
855
		$primaryKeyName = '';
856
 
857
		$table = $this->getTableCasedValue($table);
858
 
859
 
860
		$savem 			  = $ADODB_FETCH_MODE;
861
		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
862
		$this->setFetchMode(ADODB_FETCH_NUM);
863
 
864
		$sql = "SELECT *
865
				  FROM syscat.indexes
866
				 WHERE tabname='$table'";
867
 
868
		$rows = $this->getAll($sql);
869
 
870
		$this->setFetchMode($savem);
871
		$ADODB_FETCH_MODE = $savem;
872
 
873
		if (empty($rows))
874
			return false;
875
 
876
		foreach ($rows as $r)
877
		{
878
 
879
			$primaryIndex = $r[7] == 'P'?1:0;
880
			if (!$primary)
881
				/*
882
				 * Primary key not requested, ignore that one
883
				 */
884
				if ($r[7] == 'P')
885
					continue;
886
 
887
			$indexName = $this->getMetaCasedValue($r[1]);
888
			if (!isset($indices[$indexName]))
889
			{
890
				$unique = ($r[7] == 'U')?1:0;
891
				$indices[$indexName] = array('unique'=>$unique,
892
											 'primary'=>$primaryIndex,
893
											 'columns'=>array()
894
										);
895
			}
896
			$cols = explode('+',$r[6]);
897
			foreach ($cols as $colIndex=>$col)
898
			{
899
				if ($colIndex == 0)
900
					continue;
901
				$columnName = $this->getMetaCasedValue($col);
902
				$indices[$indexName]['columns'][] = $columnName;
903
			}
904
 
905
		}
906
 
907
		return $indices;
908
 
909
	}
910
 
911
	/**
912
	 * List procedures or functions in an array.
913
	 *
914
	 * We interrogate syscat.routines instead of calling the PHP
915
	 * function procedures because ADOdb requires the type of procedure
916
	 * this is not available in the php function
917
	 *
918
	 * @param	string $procedureNamePattern (optional)
919
	 * @param	string $catalog				 (optional)
920
	 * @param	string $schemaPattern		 (optional)
921
 
922
	 * @return array of procedures on current database.
923
	 *
924
	 */
925
	public function metaProcedures($procedureNamePattern = null, $catalog  = null, $schemaPattern  = null) {
926
 
927
 
928
		global $ADODB_FETCH_MODE;
929
 
930
		$metaProcedures = array();
931
		$procedureSQL   = '';
932
		$catalogSQL     = '';
933
		$schemaSQL      = '';
934
 
935
		$savem 			  = $ADODB_FETCH_MODE;
936
		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
937
 
938
		if ($procedureNamePattern)
939
			$procedureSQL = "AND ROUTINENAME LIKE " . strtoupper($this->qstr($procedureNamePattern));
940
 
941
		if ($catalog)
942
			$catalogSQL = "AND OWNER=" . strtoupper($this->qstr($catalog));
943
 
944
		if ($schemaPattern)
945
			$schemaSQL = "AND ROUTINESCHEMA LIKE {$this->qstr($schemaPattern)}";
946
 
947
 
948
		$fields = "
949
		ROUTINENAME,
950
		CASE ROUTINETYPE
951
			 WHEN 'P' THEN 'PROCEDURE'
952
			 WHEN 'F' THEN 'FUNCTION'
953
			 ELSE 'METHOD'
954
			 END AS ROUTINETYPE_NAME,
955
		ROUTINESCHEMA,
956
		REMARKS";
957
 
958
		$SQL = "SELECT $fields
959
				  FROM syscat.routines
960
				 WHERE OWNER IS NOT NULL
961
				  $procedureSQL
962
				  $catalogSQL
963
				  $schemaSQL
964
				ORDER BY ROUTINENAME
965
				";
966
 
967
		$result = $this->execute($SQL);
968
 
969
		$ADODB_FETCH_MODE = $savem;
970
 
971
		if (!$result)
972
			return false;
973
 
974
		while ($r = $result->fetchRow()){
975
			$procedureName = $this->getMetaCasedValue($r[0]);
976
			$schemaName    = $this->getMetaCasedValue($r[2]);
977
			$metaProcedures[$procedureName] = array('type'=> $r[1],
978
												   'catalog' => '',
979
												   'schema'  => $schemaName,
980
												   'remarks' => $r[3]
981
													);
982
		}
983
 
984
		return $metaProcedures;
985
 
986
	}
987
 
988
	/**
989
	  * Lists databases. Because instances are independent, we only know about
990
	  * the current database name
991
	  *
992
	  * @return string[]
993
	  */
994
	public function metaDatabases(){
995
 
996
		$dbName = $this->getMetaCasedValue($this->database);
997
 
998
		return (array)$dbName;
999
 
1000
	}
1001
 
1002
 
1003
 
1004
 
1005
/*
1006
See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/db2/htm/db2datetime_data_type_changes.asp
1007
/ SQL data type codes /
1008
#define	SQL_UNKNOWN_TYPE	0
1009
#define SQL_CHAR			1
1010
#define SQL_NUMERIC		 2
1011
#define SQL_DECIMAL		 3
1012
#define SQL_INTEGER		 4
1013
#define SQL_SMALLINT		5
1014
#define SQL_FLOAT		   6
1015
#define SQL_REAL			7
1016
#define SQL_DOUBLE		  8
1017
#if (DB2VER >= 0x0300)
1018
#define SQL_DATETIME		9
1019
#endif
1020
#define SQL_VARCHAR		12
1021
 
1022
 
1023
/ One-parameter shortcuts for date/time data types /
1024
#if (DB2VER >= 0x0300)
1025
#define SQL_TYPE_DATE	  91
1026
#define SQL_TYPE_TIME	  92
1027
#define SQL_TYPE_TIMESTAMP 93
1028
 
1029
#define SQL_UNICODE                             (-95)
1030
#define SQL_UNICODE_VARCHAR                     (-96)
1031
#define SQL_UNICODE_LONGVARCHAR                 (-97)
1032
*/
1033
	function DB2Types($t)
1034
	{
1035
		switch ((integer)$t) {
1036
		case 1:
1037
		case 12:
1038
		case 0:
1039
		case -95:
1040
		case -96:
1041
			return 'C';
1042
		case -97:
1043
		case -1: //text
1044
			return 'X';
1045
		case -4: //image
1046
			return 'B';
1047
 
1048
		case 9:
1049
		case 91:
1050
			return 'D';
1051
 
1052
		case 10:
1053
		case 11:
1054
		case 92:
1055
		case 93:
1056
			return 'T';
1057
 
1058
		case 4:
1059
		case 5:
1060
		case -6:
1061
			return 'I';
1062
 
1063
		case -11: // uniqidentifier
1064
			return 'R';
1065
		case -7: //bit
1066
			return 'L';
1067
 
1068
		default:
1069
			return 'N';
1070
		}
1071
	}
1072
 
1073
	public function metaColumns($table, $normalize=true)
1074
	{
1075
		global $ADODB_FETCH_MODE;
1076
 
1077
		$savem = $ADODB_FETCH_MODE;
1078
 
1079
		$schema = '%';
1080
		$this->_findschema($table,$schema);
1081
		$table = $this->getTableCasedValue($table);
1082
		$colname = "%";
1083
		$qid = db2_columns($this->_connectionID, null, $schema, $table, $colname);
1084
		if (empty($qid))
1085
		{
1086
			if ($this->debug)
1087
			{
1088
				$errorMessage = @db2_conn_errormsg($this->_connectionID);
1089
				ADOConnection::outp($errorMessage);
1090
			}
1091
			return false;
1092
		}
1093
 
1094
		$rs = new ADORecordSet_db2($qid);
1095
 
1096
		if (!$rs)
1097
			return false;
1098
 
1099
		$rs->_fetch();
1100
 
1101
		$retarr = array();
1102
 
1103
		/*
1104
		$rs->fields indices
1105
 
1106
		1 TABLE_SCHEM
1107
		2 TABLE_NAME
1108
		3 COLUMN_NAME
1109
		4 DATA_TYPE
1110
		5 TYPE_NAME
1111
		6 PRECISION
1112
		7 LENGTH
1113
		8 SCALE
1114
		9 RADIX
1115
		10 NULLABLE
1116
		11 REMARKS
1117
		12 Column Default
1118
		13 SQL Data Type
1119
		14 SQL DateTime SubType
1120
		15 Max length in Octets
1121
		16 Ordinal Position
1122
		17 Is NULLABLE
1123
		*/
1124
		while (!$rs->EOF)
1125
		{
1126
			if ($rs->fields[2] == $table)
1127
			{
1128
 
1129
				$fld       = new ADOFieldObject();
1130
				$fld->name = $rs->fields[3];
1131
				$fld->type = $this->DB2Types($rs->fields[4]);
1132
 
1133
				// ref: http://msdn.microsoft.com/library/default.asp?url=/archive/en-us/dnaraccgen/html/msdn_odk.asp
1134
				// access uses precision to store length for char/varchar
1135
 
1136
				if ($fld->type == 'C' or $fld->type == 'X') {
1137
					if ($rs->fields[4] <= -95) // UNICODE
1138
						$fld->max_length = $rs->fields[7]/2;
1139
					else
1140
						$fld->max_length = $rs->fields[7];
1141
				} else
1142
					$fld->max_length = $rs->fields[7];
1143
 
1144
				$fld->not_null         = !empty($rs->fields[10]);
1145
				$fld->scale            = $rs->fields[8];
1146
				$fld->primary_key      = false;
1147
 
1148
				//$columnName = $this->getMetaCasedValue($fld->name);
1149
				$columnName = strtoupper($fld->name);
1150
				$retarr[$columnName] = $fld;
1151
 
1152
			}
1153
			else if (sizeof($retarr)>0)
1154
				break;
1155
 
1156
			$rs->MoveNext();
1157
 
1158
		}
1159
 
1160
		$rs->Close();
1161
		if (empty($retarr))
1162
			$retarr = false;
1163
 
1164
		/*
1165
		 * Now we find out if the column is part of a primary key
1166
		 */
1167
 
1168
		$qid = @db2_primary_keys($this->_connectionID, "", $schema, $table);
1169
		if (empty($qid))
1170
			return false;
1171
 
1172
		$rs = new ADORecordSet_db2($qid);
1173
 
1174
		if (!$rs)
1175
		{
1176
			$ADODB_FETCH_MODE = $savem;
1177
			return $retarr;
1178
		}
1179
		$rs->_fetch();
1180
 
1181
		/*
1182
		$rs->fields indices
1183
 
1184
		1 TABLE_SCHEM
1185
		2 TABLE_NAME
1186
		3 COLUMN_NAME
1187
		4 KEY_SEQ
1188
		5 PK_NAME
1189
		*/
1190
		while (!$rs->EOF) {
1191
			if (strtoupper(trim($rs->fields[2])) == $table
1192
			&& (!$schema || strtoupper($rs->fields[1]) == $schema))
1193
			{
1194
				$retarr[strtoupper($rs->fields[3])]->primary_key = true;
1195
			}
1196
			else if (sizeof($retarr)>0)
1197
				break;
1198
 
1199
			$rs->MoveNext();
1200
		}
1201
		$rs->Close();
1202
 
1203
		$ADODB_FETCH_MODE = $savem;
1204
 
1205
		if (empty($retarr))
1206
			return false;
1207
 
1208
		/*
1209
		* If the fetch mode is numeric, return as numeric array
1210
		*/
1211
		if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM)
1212
			$retarr = array_values($retarr);
1213
 
1214
		return $retarr;
1215
	}
1216
 
1217
	/**
1218
	  * In this version if prepareSp, we just check to make sure
1219
	  * that the name of the stored procedure is correct
1220
	  * If true, we returns an array
1221
	  * else false
1222
	  *
1223
	  * @param	string	$procedureName
1224
	  * @param	mixed   $parameters (not used in db2 connections)
1225
	  * @return mixed[]
1226
	  */
1227
	function prepareSp($procedureName,$parameters=false) {
1228
 
1229
		global $ADODB_FETCH_MODE;
1230
 
1231
		$this->storedProcedureParameters = array('name'=>'',
1232
												 'resource'=>false,
1233
												 'in'=>array(),
1234
												 'out'=>array(),
1235
												 'index'=>array(),
1236
												 'parameters'=>array(),
1237
												 'keyvalue' => array());
1238
 
1239
		//$procedureName = strtoupper($procedureName);
1240
		//$procedureName = $this->getTableCasedValue($procedureName);
1241
 
1242
		$savem = $ADODB_FETCH_MODE;
1243
		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
1244
 
1245
		$qid = db2_procedures($this->_connectionID, NULL , '%' , $procedureName );
1246
 
1247
		$ADODB_FETCH_MODE = $savem;
1248
 
1249
		if (!$qid)
1250
		{
1251
			if ($this->debug)
1252
				ADOConnection::outp(sprintf('No Procedure of name %s available',$procedureName));
1253
			return false;
1254
		}
1255
 
1256
 
1257
 
1258
		$this->storedProcedureParameters['name'] = $procedureName;
1259
		/*
1260
		 * Now we know we have a valid procedure name, lets see if it requires
1261
		 * parameters
1262
		 */
1263
		$savem = $ADODB_FETCH_MODE;
1264
		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
1265
 
1266
		$qid = db2_procedure_columns($this->_connectionID, NULL , '%' , $procedureName , NULL );
1267
 
1268
		$ADODB_FETCH_MODE = $savem;
1269
 
1270
		if (!$qid)
1271
		{
1272
			if ($this->debug)
1273
				ADOConnection::outp(sprintf('No columns of name %s available',$procedureName));
1274
			return false;
1275
		}
1276
		$rs = new ADORecordSet_db2($qid);
1277
		if (!$rs)
1278
			return false;
1279
 
1280
		$preparedStatement = 'CALL %s(%s)';
1281
		$parameterMarkers = array();
1282
		while (!$rs->EOF)
1283
		{
1284
			$parameterName = $rs->fields[3];
1285
			if ($parameterName == '')
1286
			{
1287
				$rs->moveNext();
1288
				continue;
1289
			}
1290
			$parameterType = $rs->fields[4];
1291
			$ordinalPosition = $rs->fields[17];
1292
			switch($parameterType)
1293
			{
1294
			case DB2_PARAM_IN:
1295
			case DB2_PARAM_INOUT:
1296
				$this->storedProcedureParameters['in'][$parameterName] = '';
1297
				break;
1298
			case DB2_PARAM_INOUT:
1299
			case DB2_PARAM_OUT:
1300
				$this->storedProcedureParameters['out'][$parameterName] = '';
1301
				break;
1302
			}
1303
			$this->storedProcedureParameters['index'][$parameterName] = $ordinalPosition;
1304
			$this->storedProcedureParameters['parameters'][$ordinalPosition] = $rs->fields;
1305
			$rs->moveNext();
1306
 
1307
		}
1308
		$parameterCount = count($this->storedProcedureParameters['index']);
1309
		$parameterMarkers = array_fill(0,$parameterCount,'?');
1310
 
1311
		/*
1312
		 * We now know how many parameters to bind to the stored procedure
1313
		 */
1314
		$parameterList = implode(',',$parameterMarkers);
1315
 
1316
		$sql = sprintf($preparedStatement,$procedureName,$parameterList);
1317
 
1318
		$spResource = @db2_prepare($this->_connectionID,$sql);
1319
 
1320
		if (!$spResource)
1321
		{
1322
			$errorMessage = @db2_conn_errormsg($this->_connectionID);
1323
			$this->_errorMsg = $errorMessage;
1324
 
1325
			if ($this->debug)
1326
				ADOConnection::outp($errorMessage);
1327
 
1328
			return false;
1329
		}
1330
 
1331
		$this->storedProcedureParameters['resource'] = $spResource;
1332
 
1333
		if ($this->debug)
1334
		{
1335
 
1336
			ADOConnection::outp('The following parameters will be used in the SP call');
1337
			ADOConnection::outp(print_r($this->storedProcedureParameters));
1338
		}
1339
		/*
1340
		 * We now have a stored parameter resource
1341
		 * to bind to. The spResource and sql that is returned are
1342
		 * not usable, its for dummy compatibility. Everything
1343
		 * will be handled by the storedProcedureParameters
1344
		 * array
1345
		 */
1346
		return array($sql,$spResource);
1347
 
1348
	}
1349
 
1350
	private function storedProcedureParameter(&$stmt,
1351
											  &$var,
1352
											  $name,
1353
											  $isOutput=false,
1354
											  $maxLen=4000,
1355
											  $type=false)
1356
	{
1357
 
1358
 
1359
		$name = strtoupper($name);
1360
 
1361
		/*
1362
		 * Must exist in the list of parameter names for the type
1363
		 */
1364
		if ($isOutput
1365
		&& !isset( $this->storedProcedureParameters['out'][$name]))
1366
		{
1367
			$errorMessage = sprintf('%s is not a valid OUT parameter name',$name);
1368
 
1369
			$this->_errorMsg = $errorMessage;
1370
			if ($this->debug)
1371
				ADOConnection::outp($errorMessage);
1372
			return false;
1373
		}
1374
 
1375
		if (!$isOutput
1376
		&& !isset( $this->storedProcedureParameters['in'][$name]))
1377
		{
1378
			$errorMessage = sprintf('%s is not a valid IN parameter name',$name);
1379
 
1380
			$this->_errorMsg = $errorMessage;
1381
			if ($this->debug)
1382
				ADOConnection::outp($errorMessage);
1383
			return false;
1384
		}
1385
 
1386
		/*
1387
		 * We will use these values to bind to when we execute
1388
		 * the query
1389
		 */
1390
		$this->storedProcedureParameters['keyvalue'][$name] = &$var;
1391
 
1392
		return true;
1393
 
1394
	}
1395
 
1396
	/**
1397
	* Executes a prepared stored procedure.
1398
	*
1399
	* The function uses the previously accumulated information and
1400
	* resources in the $storedProcedureParameters array
1401
	*
1402
	* @return mixed	The statement id if successful, or false
1403
	*/
1404
	private function executeStoredProcedure()
1405
	{
1406
 
1407
		/*
1408
		 * Get the previously built resource
1409
		 */
1410
		$stmtid = $this->storedProcedureParameters['resource'];
1411
 
1412
		/*
1413
		 * Bind our variables to the DB2 procedure
1414
		 */
1415
		foreach ($this->storedProcedureParameters['keyvalue'] as $spName=>$spValue){
1416
 
1417
			/*
1418
			 * Get the ordinal position, required for binding
1419
			 */
1420
			$ordinalPosition = $this->storedProcedureParameters['index'][$spName];
1421
 
1422
			/*
1423
			 * Get the db2 column dictionary for the parameter
1424
			 */
1425
			$columnDictionary = $this->storedProcedureParameters['parameters'][$ordinalPosition];
1426
			$parameterType    = $columnDictionary[4];
1427
			$dataType         = $columnDictionary[5];
1428
			$precision        = $columnDictionary[10];
1429
			$scale        	  = $columnDictionary[9];
1430
 
1431
			$ok = @db2_bind_param ($this->storedProcedureParameters['resource'],
1432
								  $ordinalPosition ,
1433
								  $spName,
1434
								  $parameterType,
1435
								  $dataType,
1436
								  $precision,
1437
								  $scale
1438
								  );
1439
 
1440
			if (!$ok)
1441
			{
1442
				$this->_errorMsg  = @db2_stmt_errormsg();
1443
				$this->_errorCode = @db2_stmt_error();
1444
 
1445
				if ($this->debug)
1446
					ADOConnection::outp($this->_errorMsg);
1447
				return false;
1448
			}
1449
 
1450
			if ($this->debug)
1451
				ADOConnection::outp("Correctly Bound parameter $spName to procedure");
1452
 
1453
			/*
1454
			 * Build a variable in the current environment that matches
1455
			 * the parameter name
1456
			 */
1457
			${$spName} = $spValue;
1458
 
1459
		}
1460
 
1461
		/*
1462
		 * All bound, execute
1463
		 */
1464
 
1465
		if (!@db2_execute($stmtid))
1466
		{
1467
			$this->_errorMsg = @db2_stmt_errormsg();
1468
			$this->_errorCode = @db2_stmt_error();
1469
 
1470
			if ($this->debug)
1471
				ADOConnection::outp($this->_errorMsg);
1472
			return false;
1473
		}
1474
 
1475
		/*
1476
		 * We now take the changed parameters back into the
1477
		 * stored procedures array where we can query them later
1478
		 * Remember that $spValue was passed in by reference, so we
1479
		 * can access the value in the variable that was originally
1480
		 * passed to inParameter or outParameter
1481
		 */
1482
		foreach ($this->storedProcedureParameters['keyvalue'] as $spName=>$spValue)
1483
		{
1484
			/*
1485
			 * We make it available to the environment
1486
			 */
1487
			$spValue = ${$spName};
1488
			$this->storedProcedureParameters['keyvalue'][$spName] = $spValue;
1489
		}
1490
 
1491
		return $stmtid;
1492
	}
1493
 
1494
	/**
1495
	 *
1496
	 * Accepts an input or output parameter to bind to either a stored
1497
	 * or prepared statements. For DB2, this should not be called as an
1498
	 * API. always wrap with inParameter and outParameter
1499
	 *
1500
	 * @param mixed[] $stmt 		Statement returned by Prepare() or PrepareSP().
1501
	 * @param mixed   $var 		PHP variable to bind to. Can set to null (for isNull support).
1502
	 * @param string  $name 		Name of stored procedure variable name to bind to.
1503
	 * @param int	 $isOutput 	optional) Indicates direction of parameter
1504
	 * 							0/false=IN  1=OUT  2= IN/OUT
1505
	 *							This is ignored for Stored Procedures
1506
	 * @param int	$maxLen		(optional)Holds an maximum length of the variable.
1507
	 *							This is ignored for Stored Procedures
1508
	 * @param int	$type 		(optional) The data type of $var.
1509
	 *							This is ignored for Stored Procedures
1510
	 *
1511
	 * @return bool				Success of the operation
1512
	 */
1513
	public function parameter(&$stmt, &$var, $name, $isOutput=false, $maxLen=4000, $type=false)
1514
	{
1515
 
1516
		/*
1517
		 * If the $stmt is the name of a stored procedure we are
1518
		 * setting up, we will process it one way, otherwise
1519
		 * we assume we are setting up a prepared statement
1520
		*/
1521
		if (is_array($stmt))
1522
		{
1523
			if ($this->debug)
1524
				ADOConnection::outp("Adding parameter to stored procedure");
1525
			if ($stmt[1] == $this->storedProcedureParameters['resource'])
1526
				return $this->storedProcedureParameter($stmt[1],
1527
														$var,
1528
														$name,
1529
														$isOutput,
1530
														$maxLen,
1531
														$type);
1532
 
1533
		}
1534
 
1535
		/*
1536
		 * We are going to add a parameter to a prepared statement
1537
		 */
1538
		if ($this->debug)
1539
			ADOConnection::outp("Adding parameter to prepared statement");
1540
	}
1541
 
1542
 
1543
	/**
1544
	 * Prepares a prepared SQL statement, not used for stored procedures
1545
	 *
1546
	 * @param string	$sql
1547
	 *
1548
	 * @return mixed
1549
	 */
1550
	function prepare($sql)
1551
	{
1552
 
1553
		if (! $this->_bindInputArray) return $sql; // no binding
1554
 
1555
		$stmt = @db2_prepare($this->_connectionID,$sql);
1556
		if (!$stmt) {
1557
			// we don't know whether db2 driver is parsing prepared stmts, so just return sql
1558
			return $sql;
1559
		}
1560
		return array($sql,$stmt,false);
1561
	}
1562
 
1563
	/**
1564
	 * Executes a query
1565
	 *
1566
	 * @param	mixed $sql
1567
	 * @param	mixed $inputarr	An optional array of parameters
1568
	 *
1569
	 * @return mixed				either the queryID or false
1570
	 */
1571
	function _query($sql, $inputarr = false)
1572
	{
1573
		$db2Options = array();
1574
		/*
1575
		 * Use DB2 Internal case handling for best speed
1576
		 */
1577
		switch(ADODB_ASSOC_CASE)
1578
		{
1579
		case ADODB_ASSOC_CASE_UPPER:
1580
			$db2Options = array('db2_attr_case'=>DB2_CASE_UPPER);
1581
			$setOption = @db2_set_option($this->_connectionID,$db2Options,1);
1582
			break;
1583
 
1584
		 case ADODB_ASSOC_CASE_LOWER:
1585
			$db2Options = array('db2_attr_case'=>DB2_CASE_LOWER);
1586
			$setOption = @db2_set_option($this->_connectionID,$db2Options,1);
1587
			break;
1588
 
1589
		default:
1590
			$db2Options = array('db2_attr_case'=>DB2_CASE_NATURAL);
1591
			$setOption = @db2_set_option($this->_connectionID,$db2Options,1);
1592
		}
1593
 
1594
		if ($inputarr)
1595
		{
1596
			if (is_array($sql))
1597
			{
1598
				$stmtid = $sql[1];
1599
			}
1600
			else
1601
			{
1602
				$stmtid = @db2_prepare($this->_connectionID,$sql);
1603
 
1604
				if ($stmtid == false)
1605
				{
1606
					$this->_errorMsg  = @db2_stmt_errormsg();
1607
					$this->_errorCode = @db2_stmt_error();
1608
 
1609
					if ($this->debug)
1610
						ADOConnection::outp($this->_errorMsg);
1611
 
1612
					return false;
1613
				}
1614
			}
1615
 
1616
			if (! @db2_execute($stmtid,$inputarr))
1617
			{
1618
				$this->_errorMsg = @db2_stmt_errormsg();
1619
				$this->_errorCode = @db2_stmt_error();
1620
				if ($this->debug)
1621
					ADOConnection::outp($this->_errorMsg);
1622
				return false;
1623
			}
1624
 
1625
		}
1626
		else if (is_array($sql))
1627
		{
1628
 
1629
			/*
1630
			 * Either a prepared statement or a stored procedure
1631
			 */
1632
 
1633
			if (is_array($this->storedProcedureParameters)
1634
				&& is_resource($this->storedProcedureParameters['resource']
1635
			))
1636
				/*
1637
				 * This is all handled in the separate method for
1638
				 * readability
1639
				 */
1640
				return $this->executeStoredProcedure();
1641
 
1642
			/*
1643
			 * First, we prepare the statement
1644
			 */
1645
			$stmtid = @db2_prepare($this->_connectionID,$sql[0]);
1646
			if (!$stmtid){
1647
				$this->_errorMsg = @db2_stmt_errormsg();
1648
				$this->_errorCode = @db2_stmt_error();
1649
				if ($this->debug)
1650
					ADOConnection::outp("Prepare failed: " . $this->_errorMsg);
1651
 
1652
				return false;
1653
			}
1654
			/*
1655
			 * We next bind some input parameters
1656
			 */
1657
			$ordinal = 1;
1658
			foreach ($sql[1] as $psVar=>$psVal){
1659
				${$psVar} = $psVal;
1660
				$ok = @db2_bind_param($stmtid, $ordinal, $psVar, DB2_PARAM_IN);
1661
				if (!$ok)
1662
				{
1663
					$this->_errorMsg = @db2_stmt_errormsg();
1664
					$this->_errorCode = @db2_stmt_error();
1665
					if ($this->debug)
1666
						ADOConnection::outp("Bind failed: " . $this->_errorMsg);
1667
					return false;
1668
				}
1669
			}
1670
 
1671
			if (!@db2_execute($stmtid))
1672
			{
1673
				$this->_errorMsg = @db2_stmt_errormsg();
1674
				$this->_errorCode = @db2_stmt_error();
1675
				if ($this->debug)
1676
					ADOConnection::outp($this->_errorMsg);
1677
				return false;
1678
			}
1679
 
1680
			return $stmtid;
1681
		}
1682
		else
1683
		{
1684
 
1685
			$stmtid = @db2_exec($this->_connectionID,$sql);
1686
		}
1687
		$this->_lastAffectedRows = 0;
1688
		if ($stmtid)
1689
		{
1690
			if (@db2_num_fields($stmtid) == 0)
1691
			{
1692
				$this->_lastAffectedRows = db2_num_rows($stmtid);
1693
				$stmtid = true;
1694
			}
1695
			else
1696
			{
1697
				$this->_lastAffectedRows = 0;
1698
			}
1699
 
1700
			$this->_errorMsg = '';
1701
			$this->_errorCode = 0;
1702
 
1703
		}
1704
		else
1705
		{
1706
 
1707
			$this->_errorMsg = @db2_stmt_errormsg();
1708
			$this->_errorCode = @db2_stmt_error();
1709
 
1710
		}
1711
		return $stmtid;
1712
	}
1713
 
1714
	/*
1715
		Insert a null into the blob field of the table first.
1716
		Then use UpdateBlob to store the blob.
1717
 
1718
		Usage:
1719
 
1720
		$conn->execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
1721
		$conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
1722
	*/
1723
	function updateBlob($table,$column,$val,$where,$blobtype='BLOB')
1724
	{
1725
		return $this->execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false;
1726
	}
1727
 
1728
	// returns true or false
1729
	function _close()
1730
	{
1731
		$ret = @db2_close($this->_connectionID);
1732
		$this->_connectionID = false;
1733
		return $ret;
1734
	}
1735
 
1736
	function _affectedrows()
1737
	{
1738
		return $this->_lastAffectedRows;
1739
	}
1740
 
1741
	/**
1742
	 * Gets a meta cased parameter
1743
	 *
1744
	 * Receives an input variable to be processed per the metaCasing
1745
	 * rule, and returns the same value, processed
1746
	 *
1747
	 * @param string $value
1748
	 *
1749
	 * @return string
1750
	 */
1751
	final public function getMetaCasedValue($value)
1752
	{
1753
		global $ADODB_ASSOC_CASE;
1754
 
1755
		switch($ADODB_ASSOC_CASE)
1756
		{
1757
		case ADODB_ASSOC_CASE_LOWER:
1758
			$value = strtolower($value);
1759
			break;
1760
		case ADODB_ASSOC_CASE_UPPER:
1761
			$value = strtoupper($value);
1762
			break;
1763
		}
1764
		return $value;
1765
	}
1766
 
1767
 
1768
	const TABLECASE_LOWER    =  0;
1769
	const TABLECASE_UPPER    =  1;
1770
	const TABLECASE_DEFAULT  =  2;
1771
 
1772
	/**
1773
	 * Controls the casing of the table provided to the meta functions
1774
	 */
1775
	private $tableCase = 2;
1776
 
1777
	/**
1778
	 * Sets the table case parameter
1779
	 *
1780
	 * @param int $caseOption
1781
	 * @return null
1782
	 */
1783
	final public function setTableCasing($caseOption)
1784
	{
1785
		$this->tableCase = $caseOption;
1786
	}
1787
 
1788
	/**
1789
	 * Gets the table casing parameter
1790
	 *
1791
	 * @return int $caseOption
1792
	 */
1793
	final public function getTableCasing()
1794
	{
1795
		return $this->tableCase;
1796
	}
1797
 
1798
	/**
1799
	 * Gets a table cased parameter
1800
	 *
1801
	 * Receives an input variable to be processed per the tableCasing
1802
	 * rule, and returns the same value, processed
1803
	 *
1804
	 * @param string $value
1805
	 *
1806
	 * @return string
1807
	 */
1808
	final public function getTableCasedValue($value)
1809
	{
1810
		switch($this->tableCase)
1811
		{
1812
		case self::TABLECASE_LOWER:
1813
			$value = strtolower($value);
1814
			break;
1815
		case self::TABLECASE_UPPER:
1816
			$value = strtoupper($value);
1817
			break;
1818
		}
1819
		return $value;
1820
	}
1821
 
1822
}
1823
 
1824
/*--------------------------------------------------------------------------------------
1825
	 Class Name: Recordset
1826
--------------------------------------------------------------------------------------*/
1827
 
1828
class ADORecordSet_db2 extends ADORecordSet {
1829
 
1830
	var $bind = false;
1831
	var $databaseType = "db2";
1832
	var $dataProvider = "db2";
1833
	var $useFetchArray;
1834
 
1835
	function __construct($id,$mode=false)
1836
	{
1837
		if ($mode === false) {
1838
			global $ADODB_FETCH_MODE;
1839
			$mode = $ADODB_FETCH_MODE;
1840
		}
1841
		$this->fetchMode = $mode;
1842
 
1843
		$this->_queryID = $id;
1844
	}
1845
 
1846
 
1847
	// returns the field object
1848
	function fetchField($offset = 0)
1849
	{
1850
		$o			   = new ADOFieldObject();
1851
		$o->name 	   = @db2_field_name($this->_queryID,$offset);
1852
		$o->type 	   = @db2_field_type($this->_queryID,$offset);
1853
		$o->max_length = @db2_field_width($this->_queryID,$offset);
1854
 
1855
		/*
1856
		if (ADODB_ASSOC_CASE == 0)
1857
			$o->name = strtolower($o->name);
1858
		else if (ADODB_ASSOC_CASE == 1)
1859
			$o->name = strtoupper($o->name);
1860
		*/
1861
		return $o;
1862
	}
1863
 
1864
	/* Use associative array to get fields array */
1865
	function fields($colname)
1866
	{
1867
 
1868
		if ($this->fetchMode & ADODB_FETCH_ASSOC) {
1869
			return $this->fields[$colname];
1870
		}
1871
 
1872
		if (!$this->bind) {
1873
			$this->bind = array();
1874
			for ($i=0; $i < $this->_numOfFields; $i++) {
1875
				$o = $this->FetchField($i);
1876
				$this->bind[strtoupper($o->name)] = $i;
1877
			}
1878
		}
1879
 
1880
		 return $this->fields[$this->bind[strtoupper($colname)]];
1881
	}
1882
 
1883
 
1884
	function _initrs()
1885
	{
1886
		global $ADODB_COUNTRECS;
1887
		$this->_numOfRows = ($ADODB_COUNTRECS) ? @db2_num_rows($this->_queryID) : -1;
1888
 
1889
		$this->_numOfFields = @db2_num_fields($this->_queryID);
1890
 
1891
		// some silly drivers such as db2 as/400 and intersystems cache return _numOfRows = 0
1892
 
1893
		if ($this->_numOfRows == 0)
1894
			$this->_numOfRows = -1;
1895
	}
1896
 
1897
	function _seek($row)
1898
	{
1899
		return false;
1900
	}
1901
 
1902
	function getArrayLimit($nrows,$offset=0)
1903
	{
1904
		if ($offset <= 0) {
1905
			$rs = $this->GetArray($nrows);
1906
			return $rs;
1907
		}
1908
 
1909
		$this->Move($offset);
1910
 
1911
 
1912
		$results = array();
1913
		$cnt = 0;
1914
		while (!$this->EOF && $nrows != $cnt) {
1915
			$results[$cnt++] = $this->fields;
1916
			$this->MoveNext();
1917
		}
1918
 
1919
		return $results;
1920
	}
1921
 
1922
	function moveNext()
1923
	{
1924
		if ($this->EOF || $this->_numOfRows == 0)
1925
			return false;
1926
 
1927
		$this->_currentRow++;
1928
 
1929
		$this->processCoreFetch();
1930
		return $this->processMoveRecord();
1931
 
1932
	}
1933
 
1934
	private function processCoreFetch()
1935
	{
1936
		switch ($this->fetchMode){
1937
		case ADODB_FETCH_ASSOC:
1938
 
1939
			/*
1940
			 * Associative array
1941
			 */
1942
			$this->fields = @db2_fetch_assoc($this->_queryID);
1943
			break;
1944
 
1945
		case ADODB_FETCH_BOTH:
1946
			/*
1947
			 * Fetch both numeric and Associative array
1948
			 */
1949
			$this->fields = @db2_fetch_both($this->_queryID);
1950
			break;
1951
		default:
1952
			/*
1953
			 * Numeric array
1954
			 */
1955
			$this->fields = @db2_fetch_array($this->_queryID);
1956
			break;
1957
		}
1958
	}
1959
 
1960
	private function processMoveRecord()
1961
	{
1962
		if (!$this->fields){
1963
			$this->EOF = true;
1964
			return false;
1965
		}
1966
 
1967
		return true;
1968
	}
1969
 
1970
	function _fetch()
1971
	{
1972
		$this->processCoreFetch();
1973
		if ($this->fields)
1974
			return true;
1975
 
1976
		$this->fields = false;
1977
		return false;
1978
	}
1979
 
1980
	function _close()
1981
	{
1982
		$ok = @db2_free_result($this->_queryID);
1983
		if (!$ok)
1984
		{
1985
			$this->connection->_errorMsg  = @db2_stmt_errormsg($this->_queryID);
1986
			$this->connection->_errorCode = @db2_stmt_error();
1987
 
1988
			if ($this->debug)
1989
				ADOConnection::outp($this->connection->_errorMsg);
1990
			return false;
1991
		}
1992
	}
1993
 
1994
}