1<?php
2/*
3  V5.06 16 Oct 2008   (c) 2006 John Lim (jlim#natsoft.com). All rights reserved.
4
5  This is a version of the ADODB driver for DB2.  It uses the 'ibm_db2' PECL extension
6  for PHP (http://pecl.php.net/package/ibm_db2), which in turn requires DB2 V8.2.2 or
7  higher.
8
9  Originally tested with PHP 5.1.1 and Apache 2.0.55 on Windows XP SP2.
10  More recently tested with PHP 5.1.2 and Apache 2.0.55 on Windows XP SP2.
11
12  This file was ported from "adodb-odbc.inc.php" by Larry Menard, "larry.menard#rogers.com".
13  I ripped out what I believed to be a lot of redundant or obsolete code, but there are
14  probably still some remnants of the ODBC support in this file; I'm relying on reviewers
15  of this code to point out any other things that can be removed.
16*/
17
18// security - hide paths
19if (!defined('ADODB_DIR')) die();
20
21  define("_ADODB_DB2_LAYER", 2 );
22
23/*--------------------------------------------------------------------------------------
24--------------------------------------------------------------------------------------*/
25
26
27
28
29
30class ADODB_db2 extends ADOConnection {
31	var $databaseType = "db2";
32	var $fmtDate = "'Y-m-d'";
33	var $concat_operator = '||';
34
35	var $sysTime = 'CURRENT TIME';
36	var $sysDate = 'CURRENT DATE';
37	var $sysTimeStamp = 'CURRENT TIMESTAMP';
38
39	var $fmtTimeStamp = "'Y-m-d H:i:s'";
40	var $replaceQuote = "''"; // string to use to replace quotes
41	var $dataProvider = "db2";
42	var $hasAffectedRows = true;
43
44	var $binmode = DB2_BINARY;
45
46	var $useFetchArray = false; // setting this to true will make array elements in FETCH_ASSOC mode case-sensitive
47								// breaking backward-compat
48	var $_bindInputArray = false;
49	var $_genIDSQL = "VALUES NEXTVAL FOR %s";
50	var $_genSeqSQL = "CREATE SEQUENCE %s START WITH %s NO MAXVALUE NO CYCLE";
51	var $_dropSeqSQL = "DROP SEQUENCE %s";
52	var $_autocommit = true;
53	var $_haserrorfunctions = true;
54	var $_lastAffectedRows = 0;
55	var $uCaseTables = true; // for meta* functions, uppercase table names
56	var $hasInsertID = true;
57
58
59    function _insertid()
60    {
61        return ADOConnection::GetOne('VALUES IDENTITY_VAL_LOCAL()');
62    }
63
64	function ADODB_db2()
65	{
66		$this->_haserrorfunctions = ADODB_PHPVER >= 0x4050;
67	}
68
69		// returns true or false
70	function _connect($argDSN, $argUsername, $argPassword, $argDatabasename)
71	{
72		global $php_errormsg;
73
74		if (!function_exists('db2_connect')) {
75			ADOConnection::outp("Warning: The old ODBC based DB2 driver has been renamed 'odbc_db2'. This ADOdb driver calls PHP's native db2 extension which is not installed.");
76			return null;
77		}
78		// This needs to be set before the connect().
79		// Replaces the odbc_binmode() call that was in Execute()
80		ini_set('ibm_db2.binmode', $this->binmode);
81
82		if ($argDatabasename && empty($argDSN)) {
83
84			if (stripos($argDatabasename,'UID=') && stripos($argDatabasename,'PWD=')) $this->_connectionID = db2_connect($argDatabasename,null,null);
85			else $this->_connectionID = db2_connect($argDatabasename,$argUsername,$argPassword);
86		} else {
87			if ($argDatabasename) $schema = $argDatabasename;
88			if (stripos($argDSN,'UID=') && stripos($argDSN,'PWD=')) $this->_connectionID = db2_connect($argDSN,null,null);
89			else $this->_connectionID = db2_connect($argDSN,$argUsername,$argPassword);
90		}
91		if (isset($php_errormsg)) $php_errormsg = '';
92
93		// For db2_connect(), there is an optional 4th arg.  If present, it must be
94		// an array of valid options.  So far, we don't use them.
95
96		$this->_errorMsg = @db2_conn_errormsg();
97		if (isset($this->connectStmt)) $this->Execute($this->connectStmt);
98
99		if ($this->_connectionID && isset($schema)) $this->Execute("SET SCHEMA=$schema");
100		return $this->_connectionID != false;
101	}
102
103	// returns true or false
104	function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename)
105	{
106		global $php_errormsg;
107
108		if (!function_exists('db2_connect')) return null;
109
110		// This needs to be set before the connect().
111		// Replaces the odbc_binmode() call that was in Execute()
112		ini_set('ibm_db2.binmode', $this->binmode);
113
114		if (isset($php_errormsg)) $php_errormsg = '';
115		$this->_errorMsg = isset($php_errormsg) ? $php_errormsg : '';
116
117		if ($argDatabasename && empty($argDSN)) {
118
119			if (stripos($argDatabasename,'UID=') && stripos($argDatabasename,'PWD=')) $this->_connectionID = db2_pconnect($argDatabasename,null,null);
120			else $this->_connectionID = db2_pconnect($argDatabasename,$argUsername,$argPassword);
121		} else {
122			if ($argDatabasename) $schema = $argDatabasename;
123			if (stripos($argDSN,'UID=') && stripos($argDSN,'PWD=')) $this->_connectionID = db2_pconnect($argDSN,null,null);
124			else $this->_connectionID = db2_pconnect($argDSN,$argUsername,$argPassword);
125		}
126		if (isset($php_errormsg)) $php_errormsg = '';
127
128		$this->_errorMsg = @db2_conn_errormsg();
129		if ($this->_connectionID && $this->autoRollback) @db2_rollback($this->_connectionID);
130		if (isset($this->connectStmt)) $this->Execute($this->connectStmt);
131
132		if ($this->_connectionID && isset($schema)) $this->Execute("SET SCHEMA=$schema");
133		return $this->_connectionID != false;
134	}
135
136	// format and return date string in database timestamp format
137	function DBTimeStamp($ts)
138	{
139		if (empty($ts) && $ts !== 0) return 'null';
140		if (is_string($ts)) $ts = ADORecordSet::UnixTimeStamp($ts);
141		return 'TO_DATE('.adodb_date($this->fmtTimeStamp,$ts).",'YYYY-MM-DD HH24:MI:SS')";
142	}
143
144	// Format date column in sql string given an input format that understands Y M D
145	function SQLDate($fmt, $col=false)
146	{
147	// use right() and replace() ?
148		if (!$col) $col = $this->sysDate;
149
150		/* use TO_CHAR() if $fmt is TO_CHAR() allowed fmt */
151		if ($fmt== 'Y-m-d H:i:s')
152			return 'TO_CHAR('.$col.", 'YYYY-MM-DD HH24:MI:SS')";
153
154		$s = '';
155
156		$len = strlen($fmt);
157		for ($i=0; $i < $len; $i++) {
158			if ($s) $s .= $this->concat_operator;
159			$ch = $fmt[$i];
160			switch($ch) {
161			case 'Y':
162			case 'y':
163				if ($len==1) return "year($col)";
164				$s .= "char(year($col))";
165				break;
166			case 'M':
167				if ($len==1) return "monthname($col)";
168				$s .= "substr(monthname($col),1,3)";
169				break;
170			case 'm':
171				if ($len==1) return "month($col)";
172				$s .= "right(digits(month($col)),2)";
173				break;
174			case 'D':
175			case 'd':
176				if ($len==1) return "day($col)";
177				$s .= "right(digits(day($col)),2)";
178				break;
179			case 'H':
180			case 'h':
181				if ($len==1) return "hour($col)";
182				if ($col != $this->sysDate) $s .= "right(digits(hour($col)),2)";
183				else $s .= "''";
184				break;
185			case 'i':
186			case 'I':
187				if ($len==1) return "minute($col)";
188				if ($col != $this->sysDate)
189					$s .= "right(digits(minute($col)),2)";
190					else $s .= "''";
191				break;
192			case 'S':
193			case 's':
194				if ($len==1) return "second($col)";
195				if ($col != $this->sysDate)
196					$s .= "right(digits(second($col)),2)";
197				else $s .= "''";
198				break;
199			default:
200				if ($ch == '\\') {
201					$i++;
202					$ch = substr($fmt,$i,1);
203				}
204				$s .= $this->qstr($ch);
205			}
206		}
207		return $s;
208	}
209
210
211	function ServerInfo()
212	{
213
214		if (!empty($this->host) && ADODB_PHPVER >= 0x4300) {
215			$dsn = strtoupper($this->host);
216			$first = true;
217			$found = false;
218
219			if (!function_exists('db2_data_source')) return false;
220
221			while(true) {
222
223				$rez = @db2_data_source($this->_connectionID,
224					$first ? SQL_FETCH_FIRST : SQL_FETCH_NEXT);
225				$first = false;
226				if (!is_array($rez)) break;
227				if (strtoupper($rez['server']) == $dsn) {
228					$found = true;
229					break;
230				}
231			}
232			if (!$found) return ADOConnection::ServerInfo();
233			if (!isset($rez['version'])) $rez['version'] = '';
234			return $rez;
235		} else {
236			return ADOConnection::ServerInfo();
237		}
238	}
239
240	function CreateSequence($seqname='adodbseq',$start=1)
241	{
242		if (empty($this->_genSeqSQL)) return false;
243		$ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$start));
244		if (!$ok) return false;
245		return true;
246	}
247
248	function DropSequence($seqname)
249	{
250		if (empty($this->_dropSeqSQL)) return false;
251		return $this->Execute(sprintf($this->_dropSeqSQL,$seqname));
252	}
253
254	function SelectLimit($sql,$nrows=-1,$offset=-1,$inputArr=false)
255	{
256		$nrows = (integer) $nrows;
257		if ($offset <= 0) {
258		// could also use " OPTIMIZE FOR $nrows ROWS "
259			if ($nrows >= 0) $sql .=  " FETCH FIRST $nrows ROWS ONLY ";
260			$rs = $this->Execute($sql,$inputArr);
261		} else {
262			if ($offset > 0 && $nrows < 0);
263			else {
264				$nrows += $offset;
265				$sql .=  " FETCH FIRST $nrows ROWS ONLY ";
266			}
267			$rs = ADOConnection::SelectLimit($sql,-1,$offset,$inputArr);
268		}
269
270		return $rs;
271	}
272
273	/*
274		This algorithm is not very efficient, but works even if table locking
275		is not available.
276
277		Will return false if unable to generate an ID after $MAXLOOPS attempts.
278	*/
279	function GenID($seq='adodbseq',$start=1)
280	{
281		// if you have to modify the parameter below, your database is overloaded,
282		// or you need to implement generation of id's yourself!
283				$num = $this->GetOne("VALUES NEXTVAL FOR $seq");
284				return $num;
285	}
286
287
288	function ErrorMsg()
289	{
290		if ($this->_haserrorfunctions) {
291			if ($this->_errorMsg !== false) return $this->_errorMsg;
292			if (empty($this->_connectionID)) return @db2_conn_errormsg();
293			return @db2_conn_errormsg($this->_connectionID);
294		} else return ADOConnection::ErrorMsg();
295	}
296
297	function ErrorNo()
298	{
299
300		if ($this->_haserrorfunctions) {
301			if ($this->_errorCode !== false) {
302				// bug in 4.0.6, error number can be corrupted string (should be 6 digits)
303				return (strlen($this->_errorCode)<=2) ? 0 : $this->_errorCode;
304			}
305
306			if (empty($this->_connectionID)) $e = @db2_conn_error();
307			else $e = @db2_conn_error($this->_connectionID);
308
309			 // bug in 4.0.6, error number can be corrupted string (should be 6 digits)
310			 // so we check and patch
311			if (strlen($e)<=2) return 0;
312			return $e;
313		} else return ADOConnection::ErrorNo();
314	}
315
316
317
318	function BeginTrans()
319	{
320		if (!$this->hasTransactions) return false;
321		if ($this->transOff) return true;
322		$this->transCnt += 1;
323		$this->_autocommit = false;
324		return db2_autocommit($this->_connectionID,false);
325	}
326
327	function CommitTrans($ok=true)
328	{
329		if ($this->transOff) return true;
330		if (!$ok) return $this->RollbackTrans();
331		if ($this->transCnt) $this->transCnt -= 1;
332		$this->_autocommit = true;
333		$ret = db2_commit($this->_connectionID);
334		db2_autocommit($this->_connectionID,true);
335		return $ret;
336	}
337
338	function RollbackTrans()
339	{
340		if ($this->transOff) return true;
341		if ($this->transCnt) $this->transCnt -= 1;
342		$this->_autocommit = true;
343		$ret = db2_rollback($this->_connectionID);
344		db2_autocommit($this->_connectionID,true);
345		return $ret;
346	}
347
348	function MetaPrimaryKeys($table)
349	{
350	global $ADODB_FETCH_MODE;
351
352		if ($this->uCaseTables) $table = strtoupper($table);
353		$schema = '';
354		$this->_findschema($table,$schema);
355
356		$savem = $ADODB_FETCH_MODE;
357		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
358		$qid = @db2_primarykeys($this->_connectionID,'',$schema,$table);
359
360		if (!$qid) {
361			$ADODB_FETCH_MODE = $savem;
362			return false;
363		}
364		$rs = new ADORecordSet_db2($qid);
365		$ADODB_FETCH_MODE = $savem;
366
367		if (!$rs) return false;
368
369		$arr = $rs->GetArray();
370		$rs->Close();
371		$arr2 = array();
372		for ($i=0; $i < sizeof($arr); $i++) {
373			if ($arr[$i][3]) $arr2[] = $arr[$i][3];
374		}
375		return $arr2;
376	}
377
378	function MetaForeignKeys($table, $owner = FALSE, $upper = FALSE, $asociative = FALSE )
379	{
380	global $ADODB_FETCH_MODE;
381
382		if ($this->uCaseTables) $table = strtoupper($table);
383		$schema = '';
384		$this->_findschema($table,$schema);
385
386		$savem = $ADODB_FETCH_MODE;
387		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
388		$qid = @db2_foreign_keys($this->_connectionID,'',$schema,$table);
389		if (!$qid) {
390			$ADODB_FETCH_MODE = $savem;
391			return false;
392		}
393		$rs = new ADORecordSet_db2($qid);
394
395		$ADODB_FETCH_MODE = $savem;
396		/*
397		$rs->fields indices
398		0 PKTABLE_CAT
399		1 PKTABLE_SCHEM
400		2 PKTABLE_NAME
401		3 PKCOLUMN_NAME
402		4 FKTABLE_CAT
403		5 FKTABLE_SCHEM
404		6 FKTABLE_NAME
405		7 FKCOLUMN_NAME
406		*/
407		if (!$rs) return false;
408
409		$foreign_keys = array();
410		while (!$rs->EOF) {
411			if (strtoupper(trim($rs->fields[2])) == $table && (!$schema || strtoupper($rs->fields[1]) == $schema)) {
412				if (!is_array($foreign_keys[$rs->fields[5].'.'.$rs->fields[6]]))
413					$foreign_keys[$rs->fields[5].'.'.$rs->fields[6]] = array();
414				$foreign_keys[$rs->fields[5].'.'.$rs->fields[6]][$rs->fields[7]] = $rs->fields[3];
415			}
416			$rs->MoveNext();
417		}
418
419		$rs->Close();
420		return $foreign_key;
421	}
422
423
424	function MetaTables($ttype=false,$schema=false)
425	{
426	global $ADODB_FETCH_MODE;
427
428		$savem = $ADODB_FETCH_MODE;
429		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
430		$qid = db2_tables($this->_connectionID);
431
432		$rs = new ADORecordSet_db2($qid);
433
434		$ADODB_FETCH_MODE = $savem;
435		if (!$rs) {
436			$false = false;
437			return $false;
438		}
439
440		$arr = $rs->GetArray();
441		$rs->Close();
442		$arr2 = array();
443
444		if ($ttype) {
445			$isview = strncmp($ttype,'V',1) === 0;
446		}
447		for ($i=0; $i < sizeof($arr); $i++) {
448			if (!$arr[$i][2]) continue;
449			$type = $arr[$i][3];
450			$owner = $arr[$i][1];
451			$schemaval = ($schema) ? $arr[$i][1].'.' : '';
452			if ($ttype) {
453				if ($isview) {
454					if (strncmp($type,'V',1) === 0) $arr2[] = $schemaval.$arr[$i][2];
455				} else if (strncmp($owner,'SYS',3) !== 0) $arr2[] = $schemaval.$arr[$i][2];
456			} else if (strncmp($owner,'SYS',3) !== 0) $arr2[] = $schemaval.$arr[$i][2];
457		}
458		return $arr2;
459	}
460
461/*
462See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/db2/htm/db2datetime_data_type_changes.asp
463/ SQL data type codes /
464#define	SQL_UNKNOWN_TYPE	0
465#define SQL_CHAR			1
466#define SQL_NUMERIC		 2
467#define SQL_DECIMAL		 3
468#define SQL_INTEGER		 4
469#define SQL_SMALLINT		5
470#define SQL_FLOAT		   6
471#define SQL_REAL			7
472#define SQL_DOUBLE		  8
473#if (DB2VER >= 0x0300)
474#define SQL_DATETIME		9
475#endif
476#define SQL_VARCHAR		12
477
478
479/ One-parameter shortcuts for date/time data types /
480#if (DB2VER >= 0x0300)
481#define SQL_TYPE_DATE	  91
482#define SQL_TYPE_TIME	  92
483#define SQL_TYPE_TIMESTAMP 93
484
485#define SQL_UNICODE                             (-95)
486#define SQL_UNICODE_VARCHAR                     (-96)
487#define SQL_UNICODE_LONGVARCHAR                 (-97)
488*/
489	function DB2Types($t)
490	{
491		switch ((integer)$t) {
492		case 1:
493		case 12:
494		case 0:
495		case -95:
496		case -96:
497			return 'C';
498		case -97:
499		case -1: //text
500			return 'X';
501		case -4: //image
502			return 'B';
503
504		case 9:
505		case 91:
506			return 'D';
507
508		case 10:
509		case 11:
510		case 92:
511		case 93:
512			return 'T';
513
514		case 4:
515		case 5:
516		case -6:
517			return 'I';
518
519		case -11: // uniqidentifier
520			return 'R';
521		case -7: //bit
522			return 'L';
523
524		default:
525			return 'N';
526		}
527	}
528
529	function MetaColumns($table, $normalize=true)
530	{
531	global $ADODB_FETCH_MODE;
532
533		$false = false;
534		if ($this->uCaseTables) $table = strtoupper($table);
535		$schema = '';
536		$this->_findschema($table,$schema);
537
538		$savem = $ADODB_FETCH_MODE;
539		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
540
541        	$colname = "%";
542	        $qid = db2_columns($this->_connectionID, "", $schema, $table, $colname);
543		if (empty($qid)) return $false;
544
545		$rs = new ADORecordSet_db2($qid);
546		$ADODB_FETCH_MODE = $savem;
547
548		if (!$rs) return $false;
549		$rs->_fetch();
550
551		$retarr = array();
552
553		/*
554		$rs->fields indices
555		0 TABLE_QUALIFIER
556		1 TABLE_SCHEM
557		2 TABLE_NAME
558		3 COLUMN_NAME
559		4 DATA_TYPE
560		5 TYPE_NAME
561		6 PRECISION
562		7 LENGTH
563		8 SCALE
564		9 RADIX
565		10 NULLABLE
566		11 REMARKS
567		*/
568		while (!$rs->EOF) {
569			if (strtoupper(trim($rs->fields[2])) == $table && (!$schema || strtoupper($rs->fields[1]) == $schema)) {
570				$fld = new ADOFieldObject();
571				$fld->name = $rs->fields[3];
572				$fld->type = $this->DB2Types($rs->fields[4]);
573
574				// ref: http://msdn.microsoft.com/library/default.asp?url=/archive/en-us/dnaraccgen/html/msdn_odk.asp
575				// access uses precision to store length for char/varchar
576				if ($fld->type == 'C' or $fld->type == 'X') {
577					if ($rs->fields[4] <= -95) // UNICODE
578						$fld->max_length = $rs->fields[7]/2;
579					else
580						$fld->max_length = $rs->fields[7];
581				} else
582					$fld->max_length = $rs->fields[7];
583				$fld->not_null = !empty($rs->fields[10]);
584				$fld->scale = $rs->fields[8];
585				$fld->primary_key = false;
586				$retarr[strtoupper($fld->name)] = $fld;
587			} else if (sizeof($retarr)>0)
588				break;
589			$rs->MoveNext();
590		}
591		$rs->Close();
592		if (empty($retarr)) $retarr = false;
593
594	      $qid = db2_primary_keys($this->_connectionID, "", $schema, $table);
595		if (empty($qid)) return $false;
596
597		$rs = new ADORecordSet_db2($qid);
598		$ADODB_FETCH_MODE = $savem;
599
600		if (!$rs) return $retarr;
601		$rs->_fetch();
602
603		/*
604		$rs->fields indices
605		0 TABLE_CAT
606		1 TABLE_SCHEM
607		2 TABLE_NAME
608		3 COLUMN_NAME
609		4 KEY_SEQ
610		5 PK_NAME
611		*/
612		while (!$rs->EOF) {
613			if (strtoupper(trim($rs->fields[2])) == $table && (!$schema || strtoupper($rs->fields[1]) == $schema)) {
614				$retarr[strtoupper($rs->fields[3])]->primary_key = true;
615			} else if (sizeof($retarr)>0)
616				break;
617			$rs->MoveNext();
618		}
619		$rs->Close();
620
621		if (empty($retarr)) $retarr = false;
622		return $retarr;
623	}
624
625
626	function Prepare($sql)
627	{
628		if (! $this->_bindInputArray) return $sql; // no binding
629		$stmt = db2_prepare($this->_connectionID,$sql);
630		if (!$stmt) {
631			// we don't know whether db2 driver is parsing prepared stmts, so just return sql
632			return $sql;
633		}
634		return array($sql,$stmt,false);
635	}
636
637	/* returns queryID or false */
638	function _query($sql,$inputarr=false)
639	{
640	GLOBAL $php_errormsg;
641		if (isset($php_errormsg)) $php_errormsg = '';
642		$this->_error = '';
643
644		if ($inputarr) {
645			if (is_array($sql)) {
646				$stmtid = $sql[1];
647			} else {
648				$stmtid = db2_prepare($this->_connectionID,$sql);
649
650				if ($stmtid == false) {
651					$this->_errorMsg = isset($php_errormsg) ? $php_errormsg : '';
652					return false;
653				}
654			}
655
656			if (! db2_execute($stmtid,$inputarr)) {
657				if ($this->_haserrorfunctions) {
658					$this->_errorMsg = db2_stmt_errormsg();
659					$this->_errorCode = db2_stmt_error();
660				}
661				return false;
662			}
663
664		} else if (is_array($sql)) {
665			$stmtid = $sql[1];
666			if (!db2_execute($stmtid)) {
667				if ($this->_haserrorfunctions) {
668					$this->_errorMsg = db2_stmt_errormsg();
669					$this->_errorCode = db2_stmt_error();
670				}
671				return false;
672			}
673		} else
674			$stmtid = @db2_exec($this->_connectionID,$sql);
675
676		$this->_lastAffectedRows = 0;
677		if ($stmtid) {
678			if (@db2_num_fields($stmtid) == 0) {
679				$this->_lastAffectedRows = db2_num_rows($stmtid);
680				$stmtid = true;
681			} else {
682				$this->_lastAffectedRows = 0;
683			}
684
685			if ($this->_haserrorfunctions) {
686				$this->_errorMsg = '';
687				$this->_errorCode = 0;
688			} else
689				$this->_errorMsg = isset($php_errormsg) ? $php_errormsg : '';
690		} else {
691			if ($this->_haserrorfunctions) {
692				$this->_errorMsg = db2_stmt_errormsg();
693				$this->_errorCode = db2_stmt_error();
694			} else
695				$this->_errorMsg = isset($php_errormsg) ? $php_errormsg : '';
696
697		}
698		return $stmtid;
699	}
700
701	/*
702		Insert a null into the blob field of the table first.
703		Then use UpdateBlob to store the blob.
704
705		Usage:
706
707		$conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
708		$conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
709	*/
710	function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
711	{
712		return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false;
713	}
714
715	// returns true or false
716	function _close()
717	{
718		$ret = @db2_close($this->_connectionID);
719		$this->_connectionID = false;
720		return $ret;
721	}
722
723	function _affectedrows()
724	{
725		return $this->_lastAffectedRows;
726	}
727
728}
729
730/*--------------------------------------------------------------------------------------
731	 Class Name: Recordset
732--------------------------------------------------------------------------------------*/
733
734class ADORecordSet_db2 extends ADORecordSet {
735
736	var $bind = false;
737	var $databaseType = "db2";
738	var $dataProvider = "db2";
739	var $useFetchArray;
740
741	function ADORecordSet_db2($id,$mode=false)
742	{
743		if ($mode === false) {
744			global $ADODB_FETCH_MODE;
745			$mode = $ADODB_FETCH_MODE;
746		}
747		$this->fetchMode = $mode;
748
749		$this->_queryID = $id;
750	}
751
752
753	// returns the field object
754	function FetchField($offset = -1)
755	{
756		$o= new ADOFieldObject();
757		$o->name = @db2_field_name($this->_queryID,$offset);
758		$o->type = @db2_field_type($this->_queryID,$offset);
759		$o->max_length = db2_field_width($this->_queryID,$offset);
760		if (ADODB_ASSOC_CASE == 0) $o->name = strtolower($o->name);
761		else if (ADODB_ASSOC_CASE == 1) $o->name = strtoupper($o->name);
762		return $o;
763	}
764
765	/* Use associative array to get fields array */
766	function Fields($colname)
767	{
768		if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname];
769		if (!$this->bind) {
770			$this->bind = array();
771			for ($i=0; $i < $this->_numOfFields; $i++) {
772				$o = $this->FetchField($i);
773				$this->bind[strtoupper($o->name)] = $i;
774			}
775		}
776
777		 return $this->fields[$this->bind[strtoupper($colname)]];
778	}
779
780
781	function _initrs()
782	{
783	global $ADODB_COUNTRECS;
784		$this->_numOfRows = ($ADODB_COUNTRECS) ? @db2_num_rows($this->_queryID) : -1;
785		$this->_numOfFields = @db2_num_fields($this->_queryID);
786		// some silly drivers such as db2 as/400 and intersystems cache return _numOfRows = 0
787		if ($this->_numOfRows == 0) $this->_numOfRows = -1;
788	}
789
790	function _seek($row)
791	{
792		return false;
793	}
794
795	// speed up SelectLimit() by switching to ADODB_FETCH_NUM as ADODB_FETCH_ASSOC is emulated
796	function GetArrayLimit($nrows,$offset=-1)
797	{
798		if ($offset <= 0) {
799			$rs = $this->GetArray($nrows);
800			return $rs;
801		}
802		$savem = $this->fetchMode;
803		$this->fetchMode = ADODB_FETCH_NUM;
804		$this->Move($offset);
805		$this->fetchMode = $savem;
806
807		if ($this->fetchMode & ADODB_FETCH_ASSOC) {
808			$this->fields = $this->GetRowAssoc(ADODB_ASSOC_CASE);
809		}
810
811		$results = array();
812		$cnt = 0;
813		while (!$this->EOF && $nrows != $cnt) {
814			$results[$cnt++] = $this->fields;
815			$this->MoveNext();
816		}
817
818		return $results;
819	}
820
821
822	function MoveNext()
823	{
824		if ($this->_numOfRows != 0 && !$this->EOF) {
825			$this->_currentRow++;
826
827			$this->fields = @db2_fetch_array($this->_queryID);
828			if ($this->fields) {
829				if ($this->fetchMode & ADODB_FETCH_ASSOC) {
830					$this->fields = $this->GetRowAssoc(ADODB_ASSOC_CASE);
831				}
832				return true;
833			}
834		}
835		$this->fields = false;
836		$this->EOF = true;
837		return false;
838	}
839
840	function _fetch()
841	{
842
843		$this->fields = db2_fetch_array($this->_queryID);
844		if ($this->fields) {
845			if ($this->fetchMode & ADODB_FETCH_ASSOC) {
846				$this->fields = $this->GetRowAssoc(ADODB_ASSOC_CASE);
847			}
848			return true;
849		}
850		$this->fields = false;
851		return false;
852	}
853
854	function _close()
855	{
856		return @db2_free_result($this->_queryID);
857	}
858
859}
860?>