1<?php
2/*
3 * Set tabs to 4 for best viewing.
4 *
5 * Latest version is available at http://adodb.sourceforge.net
6 *
7 * This is the main include file for ADOdb.
8 * Database specific drivers are stored in the adodb/drivers/adodb-*.inc.php
9 *
10 * The ADOdb files are formatted so that doxygen can be used to generate documentation.
11 * Doxygen is a documentation generation tool and can be downloaded from http://doxygen.org/
12 */
13
14/**
15	\mainpage
16
17	 @version V4.80 8 Mar 2006  (c) 2000-2006 John Lim (jlim#natsoft.com.my). All rights reserved.
18
19	Released under both BSD license and Lesser GPL library license. You can choose which license
20	you prefer.
21
22	PHP's database access functions are not standardised. This creates a need for a database
23	class library to hide the differences between the different database API's (encapsulate
24	the differences) so we can easily switch databases.
25
26	We currently support MySQL, Oracle, Microsoft SQL Server, Sybase, Sybase SQL Anywhere, DB2,
27	Informix, PostgreSQL, FrontBase, Interbase (Firebird and Borland variants), Foxpro, Access,
28	ADO, SAP DB, SQLite and ODBC. We have had successful reports of connecting to Progress and
29	other databases via ODBC.
30
31	Latest Download at http://adodb.sourceforge.net/
32
33 */
34
35 if (!defined('_ADODB_LAYER')) {
36 	define('_ADODB_LAYER',1);
37
38	//==============================================================================================
39	// CONSTANT DEFINITIONS
40	//==============================================================================================
41
42
43	/**
44	 * Set ADODB_DIR to the directory where this file resides...
45	 * This constant was formerly called $ADODB_RootPath
46	 */
47	if (!defined('ADODB_DIR')) define('ADODB_DIR',dirname(__FILE__));
48
49	//==============================================================================================
50	// GLOBAL VARIABLES
51	//==============================================================================================
52
53	GLOBAL
54		$ADODB_vers, 		// database version
55		$ADODB_COUNTRECS,	// count number of records returned - slows down query
56		$ADODB_CACHE_DIR,	// directory to cache recordsets
57		$ADODB_EXTENSION,   // ADODB extension installed
58		$ADODB_COMPAT_FETCH, // If $ADODB_COUNTRECS and this is true, $rs->fields is available on EOF
59	 	$ADODB_FETCH_MODE;	// DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default...
60
61	//==============================================================================================
62	// GLOBAL SETUP
63	//==============================================================================================
64
65	$ADODB_EXTENSION = defined('ADODB_EXTENSION');
66
67	//********************************************************//
68	/*
69	Controls $ADODB_FORCE_TYPE mode. Default is ADODB_FORCE_VALUE (3).
70	Used in GetUpdateSql and GetInsertSql functions. Thx to Niko, nuko#mbnet.fi
71
72 		0 = ignore empty fields. All empty fields in array are ignored.
73		1 = force null. All empty, php null and string 'null' fields are changed to sql NULL values.
74		2 = force empty. All empty, php null and string 'null' fields are changed to sql empty '' or 0 values.
75		3 = force value. Value is left as it is. Php null and string 'null' are set to sql NULL values and empty fields '' are set to empty '' sql values.
76	*/
77        define('ADODB_FORCE_IGNORE',0);
78        define('ADODB_FORCE_NULL',1);
79        define('ADODB_FORCE_EMPTY',2);
80        define('ADODB_FORCE_VALUE',3);
81    //********************************************************//
82
83
84	if (!$ADODB_EXTENSION || ADODB_EXTENSION < 4.0) {
85
86		define('ADODB_BAD_RS','<p>Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;</p>');
87
88	// allow [ ] @ ` " and . in table names
89		define('ADODB_TABLE_REGEX','([]0-9a-z_\:\"\`\.\@\[-]*)');
90
91	// prefetching used by oracle
92		if (!defined('ADODB_PREFETCH_ROWS')) define('ADODB_PREFETCH_ROWS',10);
93
94
95	/*
96	Controls ADODB_FETCH_ASSOC field-name case. Default is 2, use native case-names.
97	This currently works only with mssql, odbc, oci8po and ibase derived drivers.
98
99 		0 = assoc lowercase field names. $rs->fields['orderid']
100		1 = assoc uppercase field names. $rs->fields['ORDERID']
101		2 = use native-case field names. $rs->fields['OrderID']
102	*/
103
104		define('ADODB_FETCH_DEFAULT',0);
105		define('ADODB_FETCH_NUM',1);
106		define('ADODB_FETCH_ASSOC',2);
107		define('ADODB_FETCH_BOTH',3);
108
109		if (!defined('TIMESTAMP_FIRST_YEAR')) define('TIMESTAMP_FIRST_YEAR',100);
110
111		// PHP's version scheme makes converting to numbers difficult - workaround
112		$_adodb_ver = (float) PHP_VERSION;
113		if ($_adodb_ver >= 5.0) {
114			define('ADODB_PHPVER',0x5000);
115		} else if ($_adodb_ver > 4.299999) { # 4.3
116			define('ADODB_PHPVER',0x4300);
117		} else if ($_adodb_ver > 4.199999) { # 4.2
118			define('ADODB_PHPVER',0x4200);
119		} else if (strnatcmp(PHP_VERSION,'4.0.5')>=0) {
120			define('ADODB_PHPVER',0x4050);
121		} else {
122			define('ADODB_PHPVER',0x4000);
123		}
124	}
125
126	//if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2);
127
128
129	/**
130	 	Accepts $src and $dest arrays, replacing string $data
131	*/
132	function ADODB_str_replace($src, $dest, $data)
133	{
134		if (ADODB_PHPVER >= 0x4050) return str_replace($src,$dest,$data);
135
136		$s = reset($src);
137		$d = reset($dest);
138		while ($s !== false) {
139			$data = str_replace($s,$d,$data);
140			$s = next($src);
141			$d = next($dest);
142		}
143		return $data;
144	}
145
146	function ADODB_Setup()
147	{
148	GLOBAL
149		$ADODB_vers, 		// database version
150		$ADODB_COUNTRECS,	// count number of records returned - slows down query
151		$ADODB_CACHE_DIR,	// directory to cache recordsets
152	 	$ADODB_FETCH_MODE,
153		$ADODB_FORCE_TYPE;
154
155		$ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT;
156		$ADODB_FORCE_TYPE = ADODB_FORCE_VALUE;
157
158
159		if (!isset($ADODB_CACHE_DIR)) {
160			$ADODB_CACHE_DIR = '/tmp'; //(isset($_ENV['TMP'])) ? $_ENV['TMP'] : '/tmp';
161		} else {
162			// do not accept url based paths, eg. http:/ or ftp:/
163			if (strpos($ADODB_CACHE_DIR,'://') !== false)
164				die("Illegal path http:// or ftp://");
165		}
166
167
168		// Initialize random number generator for randomizing cache flushes
169		srand(((double)microtime())*1000000);
170
171		/**
172		 * ADODB version as a string.
173		 */
174		$ADODB_vers = 'V4.80 8 Mar 2006  (c) 2000-2006 John Lim (jlim#natsoft.com.my). All rights reserved. Released BSD & LGPL.';
175
176		/**
177		 * Determines whether recordset->RecordCount() is used.
178		 * Set to false for highest performance -- RecordCount() will always return -1 then
179		 * for databases that provide "virtual" recordcounts...
180		 */
181		if (!isset($ADODB_COUNTRECS)) $ADODB_COUNTRECS = true;
182	}
183
184
185	//==============================================================================================
186	// CHANGE NOTHING BELOW UNLESS YOU ARE DESIGNING ADODB
187	//==============================================================================================
188
189	ADODB_Setup();
190
191	//==============================================================================================
192	// CLASS ADOFieldObject
193	//==============================================================================================
194	/**
195	 * Helper class for FetchFields -- holds info on a column
196	 */
197	class ADOFieldObject {
198		var $name = '';
199		var $max_length=0;
200		var $type="";
201/*
202		// additional fields by dannym... (danny_milo@yahoo.com)
203		var $not_null = false;
204		// actually, this has already been built-in in the postgres, fbsql AND mysql module? ^-^
205		// so we can as well make not_null standard (leaving it at "false" does not harm anyways)
206
207		var $has_default = false; // this one I have done only in mysql and postgres for now ...
208			// others to come (dannym)
209		var $default_value; // default, if any, and supported. Check has_default first.
210*/
211	}
212
213
214
215	function ADODB_TransMonitor($dbms, $fn, $errno, $errmsg, $p1, $p2, &$thisConnection)
216	{
217		//print "Errorno ($fn errno=$errno m=$errmsg) ";
218		$thisConnection->_transOK = false;
219		if ($thisConnection->_oldRaiseFn) {
220			$fn = $thisConnection->_oldRaiseFn;
221			$fn($dbms, $fn, $errno, $errmsg, $p1, $p2,$thisConnection);
222		}
223	}
224
225	//==============================================================================================
226	// CLASS ADOConnection
227	//==============================================================================================
228
229	/**
230	 * Connection object. For connecting to databases, and executing queries.
231	 */
232	class ADOConnection {
233	//
234	// PUBLIC VARS
235	//
236	var $dataProvider = 'native';
237	var $databaseType = '';		/// RDBMS currently in use, eg. odbc, mysql, mssql
238	var $database = '';			/// Name of database to be used.
239	var $host = ''; 			/// The hostname of the database server
240	var $user = ''; 			/// The username which is used to connect to the database server.
241	var $password = ''; 		/// Password for the username. For security, we no longer store it.
242	var $debug = false; 		/// if set to true will output sql statements
243	var $maxblobsize = 262144; 	/// maximum size of blobs or large text fields (262144 = 256K)-- some db's die otherwise like foxpro
244	var $concat_operator = '+'; /// default concat operator -- change to || for Oracle/Interbase
245	var $substr = 'substr';		/// substring operator
246	var $length = 'length';		/// string length ofperator
247	var $random = 'rand()';		/// random function
248	var $upperCase = 'upper';		/// uppercase function
249	var $fmtDate = "'Y-m-d'";	/// used by DBDate() as the default date format used by the database
250	var $fmtTimeStamp = "'Y-m-d, h:i:s A'"; /// used by DBTimeStamp as the default timestamp fmt.
251	var $true = '1'; 			/// string that represents TRUE for a database
252	var $false = '0'; 			/// string that represents FALSE for a database
253	var $replaceQuote = "\\'"; 	/// string to use to replace quotes
254	var $nameQuote = '"';		/// string to use to quote identifiers and names
255	var $charSet=false; 		/// character set to use - only for interbase, postgres and oci8
256	var $metaDatabasesSQL = '';
257	var $metaTablesSQL = '';
258	var $uniqueOrderBy = false; /// All order by columns have to be unique
259	var $emptyDate = '&nbsp;';
260	var $emptyTimeStamp = '&nbsp;';
261	var $lastInsID = false;
262	//--
263	var $hasInsertID = false; 		/// supports autoincrement ID?
264	var $hasAffectedRows = false; 	/// supports affected rows for update/delete?
265	var $hasTop = false;			/// support mssql/access SELECT TOP 10 * FROM TABLE
266	var $hasLimit = false;			/// support pgsql/mysql SELECT * FROM TABLE LIMIT 10
267	var $readOnly = false; 			/// this is a readonly database - used by phpLens
268	var $hasMoveFirst = false;  /// has ability to run MoveFirst(), scrolling backwards
269	var $hasGenID = false; 		/// can generate sequences using GenID();
270	var $hasTransactions = true; /// has transactions
271	//--
272	var $genID = 0; 			/// sequence id used by GenID();
273	var $raiseErrorFn = false; 	/// error function to call
274	var $isoDates = false; /// accepts dates in ISO format
275	var $cacheSecs = 3600; /// cache for 1 hour
276	var $sysDate = false; /// name of function that returns the current date
277	var $sysTimeStamp = false; /// name of function that returns the current timestamp
278	var $arrayClass = 'ADORecordSet_array'; /// name of class used to generate array recordsets, which are pre-downloaded recordsets
279
280	var $noNullStrings = false; /// oracle specific stuff - if true ensures that '' is converted to ' '
281	var $numCacheHits = 0;
282	var $numCacheMisses = 0;
283	var $pageExecuteCountRows = true;
284	var $uniqueSort = false; /// indicates that all fields in order by must be unique
285	var $leftOuter = false; /// operator to use for left outer join in WHERE clause
286	var $rightOuter = false; /// operator to use for right outer join in WHERE clause
287	var $ansiOuter = false; /// whether ansi outer join syntax supported
288	var $autoRollback = false; // autoRollback on PConnect().
289	var $poorAffectedRows = false; // affectedRows not working or unreliable
290
291	var $fnExecute = false;
292	var $fnCacheExecute = false;
293	var $blobEncodeType = false; // false=not required, 'I'=encode to integer, 'C'=encode to char
294	var $rsPrefix = "ADORecordSet_";
295
296	var $autoCommit = true; 	/// do not modify this yourself - actually private
297	var $transOff = 0; 			/// temporarily disable transactions
298	var $transCnt = 0; 			/// count of nested transactions
299
300	var $fetchMode=false;
301	 //
302	 // PRIVATE VARS
303	 //
304	var $_oldRaiseFn =  false;
305	var $_transOK = null;
306	var $_connectionID	= false;	/// The returned link identifier whenever a successful database connection is made.
307	var $_errorMsg = false;		/// A variable which was used to keep the returned last error message.  The value will
308								/// then returned by the errorMsg() function
309	var $_errorCode = false;	/// Last error code, not guaranteed to be used - only by oci8
310	var $_queryID = false;		/// This variable keeps the last created result link identifier
311
312	var $_isPersistentConnection = false;	/// A boolean variable to state whether its a persistent connection or normal connection.	*/
313	var $_bindInputArray = false; /// set to true if ADOConnection.Execute() permits binding of array parameters.
314	var $_evalAll = false;
315	var $_affected = false;
316	var $_logsql = false;
317
318	/**
319	 * Constructor
320	 */
321	function ADOConnection()
322	{
323		die('Virtual Class -- cannot instantiate');
324	}
325
326	function Version()
327	{
328	global $ADODB_vers;
329
330		return (float) substr($ADODB_vers,1);
331	}
332
333	/**
334		Get server version info...
335
336		@returns An array with 2 elements: $arr['string'] is the description string,
337			and $arr[version] is the version (also a string).
338	*/
339	function ServerInfo()
340	{
341		return array('description' => '', 'version' => '');
342	}
343
344	function IsConnected()
345	{
346    	return !empty($this->_connectionID);
347	}
348
349	function _findvers($str)
350	{
351		if (preg_match('/([0-9]+\.([0-9\.])+)/',$str, $arr)) return $arr[1];
352		else return '';
353	}
354
355	/**
356	* All error messages go through this bottleneck function.
357	* You can define your own handler by defining the function name in ADODB_OUTP.
358	*/
359	function outp($msg,$newline=true)
360	{
361	global $ADODB_FLUSH,$ADODB_OUTP;
362
363		if (defined('ADODB_OUTP')) {
364			$fn = ADODB_OUTP;
365			$fn($msg,$newline);
366			return;
367		} else if (isset($ADODB_OUTP)) {
368			$fn = $ADODB_OUTP;
369			$fn($msg,$newline);
370			return;
371		}
372
373		if ($newline) $msg .= "<br>\n";
374
375		if (isset($_SERVER['HTTP_USER_AGENT']) || !$newline) echo $msg;
376		else echo strip_tags($msg);
377
378
379		if (!empty($ADODB_FLUSH) && ob_get_length() !== false) flush(); //  do not flush if output buffering enabled - useless - thx to Jesse Mullan
380
381	}
382
383	function Time()
384	{
385		$rs =& $this->_Execute("select $this->sysTimeStamp");
386		if ($rs && !$rs->EOF) return $this->UnixTimeStamp(reset($rs->fields));
387
388		return false;
389	}
390
391	/**
392	 * Connect to database
393	 *
394	 * @param [argHostname]		Host to connect to
395	 * @param [argUsername]		Userid to login
396	 * @param [argPassword]		Associated password
397	 * @param [argDatabaseName]	database
398	 * @param [forceNew]		force new connection
399	 *
400	 * @return true or false
401	 */
402	function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "", $forceNew = false)
403	{
404		if ($argHostname != "") $this->host = $argHostname;
405		if ($argUsername != "") $this->user = $argUsername;
406		if ($argPassword != "") $this->password = $argPassword; // not stored for security reasons
407		if ($argDatabaseName != "") $this->database = $argDatabaseName;
408
409		$this->_isPersistentConnection = false;
410		if ($forceNew) {
411			if ($rez=$this->_nconnect($this->host, $this->user, $this->password, $this->database)) return true;
412		} else {
413			 if ($rez=$this->_connect($this->host, $this->user, $this->password, $this->database)) return true;
414		}
415		if (isset($rez)) {
416			$err = $this->ErrorMsg();
417			if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
418			$ret = false;
419		} else {
420			$err = "Missing extension for ".$this->dataProvider;
421			$ret = 0;
422		}
423		if ($fn = $this->raiseErrorFn)
424			$fn($this->databaseType,'CONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
425
426
427		$this->_connectionID = false;
428		if ($this->debug) ADOConnection::outp( $this->host.': '.$err);
429		return $ret;
430	}
431
432	function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName)
433	{
434		return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName);
435	}
436
437
438	/**
439	 * Always force a new connection to database - currently only works with oracle
440	 *
441	 * @param [argHostname]		Host to connect to
442	 * @param [argUsername]		Userid to login
443	 * @param [argPassword]		Associated password
444	 * @param [argDatabaseName]	database
445	 *
446	 * @return true or false
447	 */
448	function NConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
449	{
450		return $this->Connect($argHostname, $argUsername, $argPassword, $argDatabaseName, true);
451	}
452
453	/**
454	 * Establish persistent connect to database
455	 *
456	 * @param [argHostname]		Host to connect to
457	 * @param [argUsername]		Userid to login
458	 * @param [argPassword]		Associated password
459	 * @param [argDatabaseName]	database
460	 *
461	 * @return return true or false
462	 */
463	function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
464	{
465		if (defined('ADODB_NEVER_PERSIST'))
466			return $this->Connect($argHostname,$argUsername,$argPassword,$argDatabaseName);
467
468		if ($argHostname != "") $this->host = $argHostname;
469		if ($argUsername != "") $this->user = $argUsername;
470		if ($argPassword != "") $this->password = $argPassword;
471		if ($argDatabaseName != "") $this->database = $argDatabaseName;
472
473		$this->_isPersistentConnection = true;
474		if ($rez = $this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true;
475		if (isset($rez)) {
476			$err = $this->ErrorMsg();
477			if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
478			$ret = false;
479		} else {
480			$err = "Missing extension for ".$this->dataProvider;
481			$ret = 0;
482		}
483		if ($fn = $this->raiseErrorFn) {
484			$fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
485		}
486
487		$this->_connectionID = false;
488		if ($this->debug) ADOConnection::outp( $this->host.': '.$err);
489		return $ret;
490	}
491
492	// Format date column in sql string given an input format that understands Y M D
493	function SQLDate($fmt, $col=false)
494	{
495		if (!$col) $col = $this->sysDate;
496		return $col; // child class implement
497	}
498
499	/**
500	 * Should prepare the sql statement and return the stmt resource.
501	 * For databases that do not support this, we return the $sql. To ensure
502	 * compatibility with databases that do not support prepare:
503	 *
504	 *   $stmt = $db->Prepare("insert into table (id, name) values (?,?)");
505	 *   $db->Execute($stmt,array(1,'Jill')) or die('insert failed');
506	 *   $db->Execute($stmt,array(2,'Joe')) or die('insert failed');
507	 *
508	 * @param sql	SQL to send to database
509	 *
510	 * @return return FALSE, or the prepared statement, or the original sql if
511	 * 			if the database does not support prepare.
512	 *
513	 */
514	function Prepare($sql)
515	{
516		return $sql;
517	}
518
519	/**
520	 * Some databases, eg. mssql require a different function for preparing
521	 * stored procedures. So we cannot use Prepare().
522	 *
523	 * Should prepare the stored procedure  and return the stmt resource.
524	 * For databases that do not support this, we return the $sql. To ensure
525	 * compatibility with databases that do not support prepare:
526	 *
527	 * @param sql	SQL to send to database
528	 *
529	 * @return return FALSE, or the prepared statement, or the original sql if
530	 * 			if the database does not support prepare.
531	 *
532	 */
533	function PrepareSP($sql,$param=true)
534	{
535		return $this->Prepare($sql,$param);
536	}
537
538	/**
539	* PEAR DB Compat
540	*/
541	function Quote($s)
542	{
543		return $this->qstr($s,false);
544	}
545
546	/**
547	 Requested by "Karsten Dambekalns" <k.dambekalns@fishfarm.de>
548	*/
549	function QMagic($s)
550	{
551		return $this->qstr($s,get_magic_quotes_gpc());
552	}
553
554	function q(&$s)
555	{
556		$s = $this->qstr($s,false);
557	}
558
559	/**
560	* PEAR DB Compat - do not use internally.
561	*/
562	function ErrorNative()
563	{
564		return $this->ErrorNo();
565	}
566
567
568   /**
569	* PEAR DB Compat - do not use internally.
570	*/
571	function nextId($seq_name)
572	{
573		return $this->GenID($seq_name);
574	}
575
576	/**
577	*	 Lock a row, will escalate and lock the table if row locking not supported
578	*	will normally free the lock at the end of the transaction
579	*
580	*  @param $table	name of table to lock
581	*  @param $where	where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock
582	*/
583	function RowLock($table,$where)
584	{
585		return false;
586	}
587
588	function CommitLock($table)
589	{
590		return $this->CommitTrans();
591	}
592
593	function RollbackLock($table)
594	{
595		return $this->RollbackTrans();
596	}
597
598	/**
599	* PEAR DB Compat - do not use internally.
600	*
601	* The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical
602	* 	for easy porting :-)
603	*
604	* @param mode	The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM
605	* @returns		The previous fetch mode
606	*/
607	function SetFetchMode($mode)
608	{
609		$old = $this->fetchMode;
610		$this->fetchMode = $mode;
611
612		if ($old === false) {
613		global $ADODB_FETCH_MODE;
614			return $ADODB_FETCH_MODE;
615		}
616		return $old;
617	}
618
619
620	/**
621	* PEAR DB Compat - do not use internally.
622	*/
623	function &Query($sql, $inputarr=false)
624	{
625		$rs = &$this->Execute($sql, $inputarr);
626		if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
627		return $rs;
628	}
629
630
631	/**
632	* PEAR DB Compat - do not use internally
633	*/
634	function &LimitQuery($sql, $offset, $count, $params=false)
635	{
636		$rs = &$this->SelectLimit($sql, $count, $offset, $params);
637		if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
638		return $rs;
639	}
640
641
642	/**
643	* PEAR DB Compat - do not use internally
644	*/
645	function Disconnect()
646	{
647		return $this->Close();
648	}
649
650	/*
651		 Returns placeholder for parameter, eg.
652		 $DB->Param('a')
653
654		 will return ':a' for Oracle, and '?' for most other databases...
655
656		 For databases that require positioned params, eg $1, $2, $3 for postgresql,
657		 	pass in Param(false) before setting the first parameter.
658	*/
659	function Param($name,$type='C')
660	{
661		return '?';
662	}
663
664	/*
665		InParameter and OutParameter are self-documenting versions of Parameter().
666	*/
667	function InParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false)
668	{
669		return $this->Parameter($stmt,$var,$name,false,$maxLen,$type);
670	}
671
672	/*
673	*/
674	function OutParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false)
675	{
676		return $this->Parameter($stmt,$var,$name,true,$maxLen,$type);
677
678	}
679
680	/*
681	Usage in oracle
682		$stmt = $db->Prepare('select * from table where id =:myid and group=:group');
683		$db->Parameter($stmt,$id,'myid');
684		$db->Parameter($stmt,$group,'group',64);
685		$db->Execute();
686
687		@param $stmt Statement returned by Prepare() or PrepareSP().
688		@param $var PHP variable to bind to
689		@param $name Name of stored procedure variable name to bind to.
690		@param [$isOutput] Indicates direction of parameter 0/false=IN  1=OUT  2= IN/OUT. This is ignored in oci8.
691		@param [$maxLen] Holds an maximum length of the variable.
692		@param [$type] The data type of $var. Legal values depend on driver.
693
694	*/
695	function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
696	{
697		return false;
698	}
699
700	/**
701		Improved method of initiating a transaction. Used together with CompleteTrans().
702		Advantages include:
703
704		a. StartTrans/CompleteTrans is nestable, unlike BeginTrans/CommitTrans/RollbackTrans.
705		   Only the outermost block is treated as a transaction.<br>
706		b. CompleteTrans auto-detects SQL errors, and will rollback on errors, commit otherwise.<br>
707		c. All BeginTrans/CommitTrans/RollbackTrans inside a StartTrans/CompleteTrans block
708		   are disabled, making it backward compatible.
709	*/
710	function StartTrans($errfn = 'ADODB_TransMonitor')
711	{
712		if ($this->transOff > 0) {
713			$this->transOff += 1;
714			return;
715		}
716
717		$this->_oldRaiseFn = $this->raiseErrorFn;
718		$this->raiseErrorFn = $errfn;
719		$this->_transOK = true;
720
721		if ($this->debug && $this->transCnt > 0) ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans");
722		$this->BeginTrans();
723		$this->transOff = 1;
724	}
725
726
727	/**
728		Used together with StartTrans() to end a transaction. Monitors connection
729		for sql errors, and will commit or rollback as appropriate.
730
731		@autoComplete if true, monitor sql errors and commit and rollback as appropriate,
732		and if set to false force rollback even if no SQL error detected.
733		@returns true on commit, false on rollback.
734	*/
735	function CompleteTrans($autoComplete = true)
736	{
737		if ($this->transOff > 1) {
738			$this->transOff -= 1;
739			return true;
740		}
741		$this->raiseErrorFn = $this->_oldRaiseFn;
742
743		$this->transOff = 0;
744		if ($this->_transOK && $autoComplete) {
745			if (!$this->CommitTrans()) {
746				$this->_transOK = false;
747				if ($this->debug) ADOConnection::outp("Smart Commit failed");
748			} else
749				if ($this->debug) ADOConnection::outp("Smart Commit occurred");
750		} else {
751			$this->_transOK = false;
752			$this->RollbackTrans();
753			if ($this->debug) ADOCOnnection::outp("Smart Rollback occurred");
754		}
755
756		return $this->_transOK;
757	}
758
759	/*
760		At the end of a StartTrans/CompleteTrans block, perform a rollback.
761	*/
762	function FailTrans()
763	{
764		if ($this->debug)
765			if ($this->transOff == 0) {
766				ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans");
767			} else {
768				ADOConnection::outp("FailTrans was called");
769				adodb_backtrace();
770			}
771		$this->_transOK = false;
772	}
773
774	/**
775		Check if transaction has failed, only for Smart Transactions.
776	*/
777	function HasFailedTrans()
778	{
779		if ($this->transOff > 0) return $this->_transOK == false;
780		return false;
781	}
782
783	/**
784	 * Execute SQL
785	 *
786	 * @param sql		SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text)
787	 * @param [inputarr]	holds the input data to bind to. Null elements will be set to null.
788	 * @return 		RecordSet or false
789	 */
790	function &Execute($sql,$inputarr=false)
791	{
792		if ($this->fnExecute) {
793			$fn = $this->fnExecute;
794			$ret =& $fn($this,$sql,$inputarr);
795			if (isset($ret)) return $ret;
796		}
797		if ($inputarr) {
798			if (!is_array($inputarr)) $inputarr = array($inputarr);
799
800			$element0 = reset($inputarr);
801			# is_object check because oci8 descriptors can be passed in
802			$array_2d = is_array($element0) && !is_object(reset($element0));
803			//remove extra memory copy of input -mikefedyk
804			unset($element0);
805
806			if (!is_array($sql) && !$this->_bindInputArray) {
807				$sqlarr = explode('?',$sql);
808
809				if (!$array_2d) $inputarr = array($inputarr);
810				foreach($inputarr as $arr) {
811					$sql = ''; $i = 0;
812					//Use each() instead of foreach to reduce memory usage -mikefedyk
813					while(list(, $v) = each($arr)) {
814						$sql .= $sqlarr[$i];
815						// from Ron Baldwin <ron.baldwin#sourceprose.com>
816						// Only quote string types
817						$typ = gettype($v);
818						if ($typ == 'string')
819							//New memory copy of input created here -mikefedyk
820							$sql .= $this->qstr($v);
821						else if ($typ == 'double')
822							$sql .= str_replace(',','.',$v); // locales fix so 1.1 does not get converted to 1,1
823						else if ($typ == 'boolean')
824							$sql .= $v ? $this->true : $this->false;
825						else if ($v === null)
826							$sql .= 'NULL';
827						else
828							$sql .= $v;
829						$i += 1;
830					}
831					if (isset($sqlarr[$i])) {
832						$sql .= $sqlarr[$i];
833						if ($i+1 != sizeof($sqlarr)) ADOConnection::outp( "Input Array does not match ?: ".htmlspecialchars($sql));
834					} else if ($i != sizeof($sqlarr))
835						ADOConnection::outp( "Input array does not match ?: ".htmlspecialchars($sql));
836
837					$ret =& $this->_Execute($sql);
838					if (!$ret) return $ret;
839				}
840			} else {
841				if ($array_2d) {
842					if (is_string($sql))
843						$stmt = $this->Prepare($sql);
844					else
845						$stmt = $sql;
846
847					foreach($inputarr as $arr) {
848						$ret =& $this->_Execute($stmt,$arr);
849						if (!$ret) return $ret;
850					}
851				} else {
852					$ret =& $this->_Execute($sql,$inputarr);
853				}
854			}
855		} else {
856			$ret =& $this->_Execute($sql,false);
857		}
858
859		return $ret;
860	}
861
862
863	function &_Execute($sql,$inputarr=false)
864	{
865		if ($this->debug) {
866			global $ADODB_INCLUDED_LIB;
867			if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php');
868			$this->_queryID = _adodb_debug_execute($this, $sql,$inputarr);
869		} else {
870			$this->_queryID = @$this->_query($sql,$inputarr);
871		}
872
873		/************************
874		// OK, query executed
875		*************************/
876
877		if ($this->_queryID === false) { // error handling if query fails
878			if ($this->debug == 99) adodb_backtrace(true,5);
879			$fn = $this->raiseErrorFn;
880			if ($fn) {
881				$fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr,$this);
882			}
883			$false = false;
884			return $false;
885		}
886
887		if ($this->_queryID === true) { // return simplified recordset for inserts/updates/deletes with lower overhead
888			$rs =& new ADORecordSet_empty();
889			return $rs;
890		}
891
892		// return real recordset from select statement
893		$rsclass = $this->rsPrefix.$this->databaseType;
894		$rs = new $rsclass($this->_queryID,$this->fetchMode);
895		$rs->connection = &$this; // Pablo suggestion
896		$rs->Init();
897		if (is_array($sql)) $rs->sql = $sql[0];
898		else $rs->sql = $sql;
899		if ($rs->_numOfRows <= 0) {
900		global $ADODB_COUNTRECS;
901			if ($ADODB_COUNTRECS) {
902				if (!$rs->EOF) {
903					$rs = &$this->_rs2rs($rs,-1,-1,!is_array($sql));
904					$rs->_queryID = $this->_queryID;
905				} else
906					$rs->_numOfRows = 0;
907			}
908		}
909		return $rs;
910	}
911
912	function CreateSequence($seqname='adodbseq',$startID=1)
913	{
914		if (empty($this->_genSeqSQL)) return false;
915		return $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
916	}
917
918	function DropSequence($seqname='adodbseq')
919	{
920		if (empty($this->_dropSeqSQL)) return false;
921		return $this->Execute(sprintf($this->_dropSeqSQL,$seqname));
922	}
923
924	/**
925	 * Generates a sequence id and stores it in $this->genID;
926	 * GenID is only available if $this->hasGenID = true;
927	 *
928	 * @param seqname		name of sequence to use
929	 * @param startID		if sequence does not exist, start at this ID
930	 * @return		0 if not supported, otherwise a sequence id
931	 */
932	function GenID($seqname='adodbseq',$startID=1)
933	{
934		if (!$this->hasGenID) {
935			return 0; // formerly returns false pre 1.60
936		}
937
938		$getnext = sprintf($this->_genIDSQL,$seqname);
939
940		$holdtransOK = $this->_transOK;
941
942		$save_handler = $this->raiseErrorFn;
943		$this->raiseErrorFn = '';
944		@($rs = $this->Execute($getnext));
945		$this->raiseErrorFn = $save_handler;
946
947		if (!$rs) {
948			$this->_transOK = $holdtransOK; //if the status was ok before reset
949			$createseq = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
950			$rs = $this->Execute($getnext);
951		}
952		if ($rs && !$rs->EOF) $this->genID = reset($rs->fields);
953		else $this->genID = 0; // false
954
955		if ($rs) $rs->Close();
956
957		return $this->genID;
958	}
959
960	/**
961	 * @param $table string name of the table, not needed by all databases (eg. mysql), default ''
962	 * @param $column string name of the column, not needed by all databases (eg. mysql), default ''
963	 * @return  the last inserted ID. Not all databases support this.
964	 */
965	function Insert_ID($table='',$column='')
966	{
967		if ($this->_logsql && $this->lastInsID) return $this->lastInsID;
968		if ($this->hasInsertID) return $this->_insertid($table,$column);
969		if ($this->debug) {
970			ADOConnection::outp( '<p>Insert_ID error</p>');
971			adodb_backtrace();
972		}
973		return false;
974	}
975
976
977	/**
978	 * Portable Insert ID. Pablo Roca <pabloroca#mvps.org>
979	 *
980	 * @return  the last inserted ID. All databases support this. But aware possible
981	 * problems in multiuser environments. Heavy test this before deploying.
982	 */
983	function PO_Insert_ID($table="", $id="")
984	{
985	   if ($this->hasInsertID){
986		   return $this->Insert_ID($table,$id);
987	   } else {
988		   return $this->GetOne("SELECT MAX($id) FROM $table");
989	   }
990	}
991
992	/**
993	* @return # rows affected by UPDATE/DELETE
994	*/
995	function Affected_Rows()
996	{
997		if ($this->hasAffectedRows) {
998			if ($this->fnExecute === 'adodb_log_sql') {
999				if ($this->_logsql && $this->_affected !== false) return $this->_affected;
1000			}
1001			$val = $this->_affectedrows();
1002			return ($val < 0) ? false : $val;
1003		}
1004
1005		if ($this->debug) ADOConnection::outp( '<p>Affected_Rows error</p>',false);
1006		return false;
1007	}
1008
1009
1010	/**
1011	 * @return  the last error message
1012	 */
1013	function ErrorMsg()
1014	{
1015		if ($this->_errorMsg) return '!! '.strtoupper($this->dataProvider.' '.$this->databaseType).': '.$this->_errorMsg;
1016		else return '';
1017	}
1018
1019
1020	/**
1021	 * @return the last error number. Normally 0 means no error.
1022	 */
1023	function ErrorNo()
1024	{
1025		return ($this->_errorMsg) ? -1 : 0;
1026	}
1027
1028	function MetaError($err=false)
1029	{
1030		include_once(ADODB_DIR."/adodb-error.inc.php");
1031		if ($err === false) $err = $this->ErrorNo();
1032		return adodb_error($this->dataProvider,$this->databaseType,$err);
1033	}
1034
1035	function MetaErrorMsg($errno)
1036	{
1037		include_once(ADODB_DIR."/adodb-error.inc.php");
1038		return adodb_errormsg($errno);
1039	}
1040
1041	/**
1042	 * @returns an array with the primary key columns in it.
1043	 */
1044	function MetaPrimaryKeys($table, $owner=false)
1045	{
1046	// owner not used in base class - see oci8
1047		$p = array();
1048		$objs =& $this->MetaColumns($table);
1049		if ($objs) {
1050			foreach($objs as $v) {
1051				if (!empty($v->primary_key))
1052					$p[] = $v->name;
1053			}
1054		}
1055		if (sizeof($p)) return $p;
1056		if (function_exists('ADODB_VIEW_PRIMARYKEYS'))
1057			return ADODB_VIEW_PRIMARYKEYS($this->databaseType, $this->database, $table, $owner);
1058		return false;
1059	}
1060
1061	/**
1062	 * @returns assoc array where keys are tables, and values are foreign keys
1063	 */
1064	function MetaForeignKeys($table, $owner=false, $upper=false)
1065	{
1066		return false;
1067	}
1068	/**
1069	 * Choose a database to connect to. Many databases do not support this.
1070	 *
1071	 * @param dbName 	is the name of the database to select
1072	 * @return 		true or false
1073	 */
1074	function SelectDB($dbName)
1075	{return false;}
1076
1077
1078	/**
1079	* Will select, getting rows from $offset (1-based), for $nrows.
1080	* This simulates the MySQL "select * from table limit $offset,$nrows" , and
1081	* the PostgreSQL "select * from table limit $nrows offset $offset". Note that
1082	* MySQL and PostgreSQL parameter ordering is the opposite of the other.
1083	* eg.
1084	*  SelectLimit('select * from table',3); will return rows 1 to 3 (1-based)
1085	*  SelectLimit('select * from table',3,2); will return rows 3 to 5 (1-based)
1086	*
1087	* Uses SELECT TOP for Microsoft databases (when $this->hasTop is set)
1088	* BUG: Currently SelectLimit fails with $sql with LIMIT or TOP clause already set
1089	*
1090	* @param sql
1091	* @param [offset]	is the row to start calculations from (1-based)
1092	* @param [nrows]		is the number of rows to get
1093	* @param [inputarr]	array of bind variables
1094	* @param [secs2cache]		is a private parameter only used by jlim
1095	* @return		the recordset ($rs->databaseType == 'array')
1096 	*/
1097	function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
1098	{
1099		if ($this->hasTop && $nrows > 0) {
1100		// suggested by Reinhard Balling. Access requires top after distinct
1101		 // Informix requires first before distinct - F Riosa
1102			$ismssql = (strpos($this->databaseType,'mssql') !== false);
1103			if ($ismssql) $isaccess = false;
1104			else $isaccess = (strpos($this->databaseType,'access') !== false);
1105
1106			if ($offset <= 0) {
1107
1108					// access includes ties in result
1109					if ($isaccess) {
1110						$sql = preg_replace(
1111						'/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1112
1113						if ($secs2cache>0) {
1114							$ret =& $this->CacheExecute($secs2cache, $sql,$inputarr);
1115						} else {
1116							$ret =& $this->Execute($sql,$inputarr);
1117						}
1118						return $ret; // PHP5 fix
1119					} else if ($ismssql){
1120						$sql = preg_replace(
1121						'/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1122					} else {
1123						$sql = preg_replace(
1124						'/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1125					}
1126			} else {
1127				$nn = $nrows + $offset;
1128				if ($isaccess || $ismssql) {
1129					$sql = preg_replace(
1130					'/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql);
1131				} else {
1132					$sql = preg_replace(
1133					'/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql);
1134				}
1135			}
1136		}
1137
1138		// if $offset>0, we want to skip rows, and $ADODB_COUNTRECS is set, we buffer  rows
1139		// 0 to offset-1 which will be discarded anyway. So we disable $ADODB_COUNTRECS.
1140		global $ADODB_COUNTRECS;
1141
1142		$savec = $ADODB_COUNTRECS;
1143		$ADODB_COUNTRECS = false;
1144
1145		if ($offset>0){
1146			if ($secs2cache>0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr);
1147			else $rs = &$this->Execute($sql,$inputarr);
1148		} else {
1149			if ($secs2cache>0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr);
1150			else $rs = &$this->Execute($sql,$inputarr);
1151		}
1152		$ADODB_COUNTRECS = $savec;
1153		if ($rs && !$rs->EOF) {
1154			$rs =& $this->_rs2rs($rs,$nrows,$offset);
1155		}
1156		//print_r($rs);
1157		return $rs;
1158	}
1159
1160	/**
1161	* Create serializable recordset. Breaks rs link to connection.
1162	*
1163	* @param rs			the recordset to serialize
1164	*/
1165	function &SerializableRS(&$rs)
1166	{
1167		$rs2 =& $this->_rs2rs($rs);
1168		$ignore = false;
1169		$rs2->connection =& $ignore;
1170
1171		return $rs2;
1172	}
1173
1174	/**
1175	* Convert database recordset to an array recordset
1176	* input recordset's cursor should be at beginning, and
1177	* old $rs will be closed.
1178	*
1179	* @param rs			the recordset to copy
1180	* @param [nrows]  	number of rows to retrieve (optional)
1181	* @param [offset] 	offset by number of rows (optional)
1182	* @return 			the new recordset
1183	*/
1184	function &_rs2rs(&$rs,$nrows=-1,$offset=-1,$close=true)
1185	{
1186		if (! $rs) {
1187			$false = false;
1188			return $false;
1189		}
1190		$dbtype = $rs->databaseType;
1191		if (!$dbtype) {
1192			$rs = &$rs;  // required to prevent crashing in 4.2.1, but does not happen in 4.3.1 -- why ?
1193			return $rs;
1194		}
1195		if (($dbtype == 'array' || $dbtype == 'csv') && $nrows == -1 && $offset == -1) {
1196			$rs->MoveFirst();
1197			$rs = &$rs; // required to prevent crashing in 4.2.1, but does not happen in 4.3.1-- why ?
1198			return $rs;
1199		}
1200		$flds = array();
1201		for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) {
1202			$flds[] = $rs->FetchField($i);
1203		}
1204
1205		$arr =& $rs->GetArrayLimit($nrows,$offset);
1206		//print_r($arr);
1207		if ($close) $rs->Close();
1208
1209		$arrayClass = $this->arrayClass;
1210
1211		$rs2 = new $arrayClass();
1212		$rs2->connection = &$this;
1213		$rs2->sql = $rs->sql;
1214		$rs2->dataProvider = $this->dataProvider;
1215		$rs2->InitArrayFields($arr,$flds);
1216		$rs2->fetchMode = isset($rs->adodbFetchMode) ? $rs->adodbFetchMode : $rs->fetchMode;
1217		return $rs2;
1218	}
1219
1220	/*
1221	* Return all rows. Compat with PEAR DB
1222	*/
1223	function &GetAll($sql, $inputarr=false)
1224	{
1225		$arr =& $this->GetArray($sql,$inputarr);
1226		return $arr;
1227	}
1228
1229	function &GetAssoc($sql, $inputarr=false,$force_array = false, $first2cols = false)
1230	{
1231		$rs =& $this->Execute($sql, $inputarr);
1232		if (!$rs) {
1233			$false = false;
1234			return $false;
1235		}
1236		$arr =& $rs->GetAssoc($force_array,$first2cols);
1237		return $arr;
1238	}
1239
1240	function &CacheGetAssoc($secs2cache, $sql=false, $inputarr=false,$force_array = false, $first2cols = false)
1241	{
1242		if (!is_numeric($secs2cache)) {
1243			$first2cols = $force_array;
1244			$force_array = $inputarr;
1245		}
1246		$rs =& $this->CacheExecute($secs2cache, $sql, $inputarr);
1247		if (!$rs) {
1248			$false = false;
1249			return $false;
1250		}
1251		$arr =& $rs->GetAssoc($force_array,$first2cols);
1252		return $arr;
1253	}
1254
1255	/**
1256	* Return first element of first row of sql statement. Recordset is disposed
1257	* for you.
1258	*
1259	* @param sql			SQL statement
1260	* @param [inputarr]		input bind array
1261	*/
1262	function GetOne($sql,$inputarr=false)
1263	{
1264	global $ADODB_COUNTRECS;
1265		$crecs = $ADODB_COUNTRECS;
1266		$ADODB_COUNTRECS = false;
1267
1268		$ret = false;
1269		$rs = &$this->Execute($sql,$inputarr);
1270		if ($rs) {
1271			if (!$rs->EOF) $ret = reset($rs->fields);
1272			$rs->Close();
1273		}
1274		$ADODB_COUNTRECS = $crecs;
1275		return $ret;
1276	}
1277
1278	function CacheGetOne($secs2cache,$sql=false,$inputarr=false)
1279	{
1280		$ret = false;
1281		$rs = &$this->CacheExecute($secs2cache,$sql,$inputarr);
1282		if ($rs) {
1283			if (!$rs->EOF) $ret = reset($rs->fields);
1284			$rs->Close();
1285		}
1286
1287		return $ret;
1288	}
1289
1290	function GetCol($sql, $inputarr = false, $trim = false)
1291	{
1292	  	$rv = false;
1293	  	$rs = &$this->Execute($sql, $inputarr);
1294	  	if ($rs) {
1295			$rv = array();
1296	   		if ($trim) {
1297				while (!$rs->EOF) {
1298					$rv[] = trim(reset($rs->fields));
1299					$rs->MoveNext();
1300		   		}
1301			} else {
1302				while (!$rs->EOF) {
1303					$rv[] = reset($rs->fields);
1304					$rs->MoveNext();
1305		   		}
1306			}
1307	   		$rs->Close();
1308	  	}
1309	  	return $rv;
1310	}
1311
1312	function CacheGetCol($secs, $sql = false, $inputarr = false,$trim=false)
1313	{
1314	  	$rv = false;
1315	  	$rs = &$this->CacheExecute($secs, $sql, $inputarr);
1316	  	if ($rs) {
1317			if ($trim) {
1318				while (!$rs->EOF) {
1319					$rv[] = trim(reset($rs->fields));
1320					$rs->MoveNext();
1321		   		}
1322			} else {
1323				while (!$rs->EOF) {
1324					$rv[] = reset($rs->fields);
1325					$rs->MoveNext();
1326		   		}
1327			}
1328	   		$rs->Close();
1329	  	}
1330	  	return $rv;
1331	}
1332
1333	/*
1334		Calculate the offset of a date for a particular database and generate
1335			appropriate SQL. Useful for calculating future/past dates and storing
1336			in a database.
1337
1338		If dayFraction=1.5 means 1.5 days from now, 1.0/24 for 1 hour.
1339	*/
1340	function OffsetDate($dayFraction,$date=false)
1341	{
1342		if (!$date) $date = $this->sysDate;
1343		return  '('.$date.'+'.$dayFraction.')';
1344	}
1345
1346
1347	/**
1348	*
1349	* @param sql			SQL statement
1350	* @param [inputarr]		input bind array
1351	*/
1352	function &GetArray($sql,$inputarr=false)
1353	{
1354	global $ADODB_COUNTRECS;
1355
1356		$savec = $ADODB_COUNTRECS;
1357		$ADODB_COUNTRECS = false;
1358		$rs =& $this->Execute($sql,$inputarr);
1359		$ADODB_COUNTRECS = $savec;
1360		if (!$rs)
1361			if (defined('ADODB_PEAR')) {
1362				$cls = ADODB_PEAR_Error();
1363				return $cls;
1364			} else {
1365				$false = false;
1366				return $false;
1367			}
1368		$arr =& $rs->GetArray();
1369		$rs->Close();
1370		return $arr;
1371	}
1372
1373	function &CacheGetAll($secs2cache,$sql=false,$inputarr=false)
1374	{
1375		return $this->CacheGetArray($secs2cache,$sql,$inputarr);
1376	}
1377
1378	function &CacheGetArray($secs2cache,$sql=false,$inputarr=false)
1379	{
1380	global $ADODB_COUNTRECS;
1381
1382		$savec = $ADODB_COUNTRECS;
1383		$ADODB_COUNTRECS = false;
1384		$rs =& $this->CacheExecute($secs2cache,$sql,$inputarr);
1385		$ADODB_COUNTRECS = $savec;
1386
1387		if (!$rs)
1388			if (defined('ADODB_PEAR')) {
1389				$cls = ADODB_PEAR_Error();
1390				return $cls;
1391			} else {
1392				$false = false;
1393				return $false;
1394			}
1395		$arr =& $rs->GetArray();
1396		$rs->Close();
1397		return $arr;
1398	}
1399
1400
1401
1402	/**
1403	* Return one row of sql statement. Recordset is disposed for you.
1404	*
1405	* @param sql			SQL statement
1406	* @param [inputarr]		input bind array
1407	*/
1408	function &GetRow($sql,$inputarr=false)
1409	{
1410	global $ADODB_COUNTRECS;
1411		$crecs = $ADODB_COUNTRECS;
1412		$ADODB_COUNTRECS = false;
1413
1414		$rs =& $this->Execute($sql,$inputarr);
1415
1416		$ADODB_COUNTRECS = $crecs;
1417		if ($rs) {
1418			if (!$rs->EOF) $arr = $rs->fields;
1419			else $arr = array();
1420			$rs->Close();
1421			return $arr;
1422		}
1423
1424		$false = false;
1425		return $false;
1426	}
1427
1428	function &CacheGetRow($secs2cache,$sql=false,$inputarr=false)
1429	{
1430		$rs =& $this->CacheExecute($secs2cache,$sql,$inputarr);
1431		if ($rs) {
1432			$arr = false;
1433			if (!$rs->EOF) $arr = $rs->fields;
1434			$rs->Close();
1435			return $arr;
1436		}
1437		$false = false;
1438		return $false;
1439	}
1440
1441	/**
1442	* Insert or replace a single record. Note: this is not the same as MySQL's replace.
1443	* ADOdb's Replace() uses update-insert semantics, not insert-delete-duplicates of MySQL.
1444	* Also note that no table locking is done currently, so it is possible that the
1445	* record be inserted twice by two programs...
1446	*
1447	* $this->Replace('products', array('prodname' =>"'Nails'","price" => 3.99), 'prodname');
1448	*
1449	* $table		table name
1450	* $fieldArray	associative array of data (you must quote strings yourself).
1451	* $keyCol		the primary key field name or if compound key, array of field names
1452	* autoQuote		set to true to use a hueristic to quote strings. Works with nulls and numbers
1453	*					but does not work with dates nor SQL functions.
1454	* has_autoinc	the primary key is an auto-inc field, so skip in insert.
1455	*
1456	* Currently blob replace not supported
1457	*
1458	* returns 0 = fail, 1 = update, 2 = insert
1459	*/
1460
1461	function Replace($table, $fieldArray, $keyCol, $autoQuote=false, $has_autoinc=false)
1462	{
1463		global $ADODB_INCLUDED_LIB;
1464		if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php');
1465
1466		return _adodb_replace($this, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc);
1467	}
1468
1469
1470	/**
1471	* Will select, getting rows from $offset (1-based), for $nrows.
1472	* This simulates the MySQL "select * from table limit $offset,$nrows" , and
1473	* the PostgreSQL "select * from table limit $nrows offset $offset". Note that
1474	* MySQL and PostgreSQL parameter ordering is the opposite of the other.
1475	* eg.
1476	*  CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based)
1477	*  CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based)
1478	*
1479	* BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set
1480	*
1481	* @param [secs2cache]	seconds to cache data, set to 0 to force query. This is optional
1482	* @param sql
1483	* @param [offset]	is the row to start calculations from (1-based)
1484	* @param [nrows]	is the number of rows to get
1485	* @param [inputarr]	array of bind variables
1486	* @return		the recordset ($rs->databaseType == 'array')
1487 	*/
1488	function &CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false)
1489	{
1490		if (!is_numeric($secs2cache)) {
1491			if ($sql === false) $sql = -1;
1492			if ($offset == -1) $offset = false;
1493									  // sql,	nrows, offset,inputarr
1494			$rs =& $this->SelectLimit($secs2cache,$sql,$nrows,$offset,$this->cacheSecs);
1495		} else {
1496			if ($sql === false) ADOConnection::outp( "Warning: \$sql missing from CacheSelectLimit()");
1497			$rs =& $this->SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
1498		}
1499		return $rs;
1500	}
1501
1502
1503	/**
1504	* Flush cached recordsets that match a particular $sql statement.
1505	* If $sql == false, then we purge all files in the cache.
1506 	*/
1507
1508	/**
1509   * Flush cached recordsets that match a particular $sql statement.
1510   * If $sql == false, then we purge all files in the cache.
1511    */
1512	function CacheFlush($sql=false,$inputarr=false)
1513	{
1514	global $ADODB_CACHE_DIR;
1515
1516		if (strlen($ADODB_CACHE_DIR) > 1 && !$sql) {
1517         /*if (strncmp(PHP_OS,'WIN',3) === 0)
1518            $dir = str_replace('/', '\\', $ADODB_CACHE_DIR);
1519         else */
1520            $dir = $ADODB_CACHE_DIR;
1521
1522         if ($this->debug) {
1523            ADOConnection::outp( "CacheFlush: $dir<br><pre>\n", $this->_dirFlush($dir),"</pre>");
1524         } else {
1525            $this->_dirFlush($dir);
1526         }
1527         return;
1528      }
1529
1530      global $ADODB_INCLUDED_CSV;
1531      if (empty($ADODB_INCLUDED_CSV)) include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
1532
1533      $f = $this->_gencachename($sql.serialize($inputarr),false);
1534      adodb_write_file($f,''); // is adodb_write_file needed?
1535      if (!@unlink($f)) {
1536         if ($this->debug) ADOConnection::outp( "CacheFlush: failed for $f");
1537      }
1538   }
1539
1540   /**
1541   * Private function to erase all of the files and subdirectories in a directory.
1542   *
1543   * Just specify the directory, and tell it if you want to delete the directory or just clear it out.
1544   * Note: $kill_top_level is used internally in the function to flush subdirectories.
1545   */
1546   function _dirFlush($dir, $kill_top_level = false) {
1547      if(!$dh = @opendir($dir)) return;
1548
1549      while (($obj = readdir($dh))) {
1550         if($obj=='.' || $obj=='..')
1551            continue;
1552
1553         if (!@unlink($dir.'/'.$obj))
1554			  $this->_dirFlush($dir.'/'.$obj, true);
1555      }
1556      if ($kill_top_level === true)
1557         @rmdir($dir);
1558      return true;
1559   }
1560
1561
1562	function xCacheFlush($sql=false,$inputarr=false)
1563	{
1564	global $ADODB_CACHE_DIR;
1565
1566		if (strlen($ADODB_CACHE_DIR) > 1 && !$sql) {
1567			if (strncmp(PHP_OS,'WIN',3) === 0) {
1568				$cmd = 'del /s '.str_replace('/','\\',$ADODB_CACHE_DIR).'\adodb_*.cache';
1569			} else {
1570				//$cmd = 'find "'.$ADODB_CACHE_DIR.'" -type f -maxdepth 1 -print0 | xargs -0 rm -f';
1571				$cmd = 'rm -rf '.$ADODB_CACHE_DIR.'/[0-9a-f][0-9a-f]/';
1572				// old version 'rm -f `find '.$ADODB_CACHE_DIR.' -name adodb_*.cache`';
1573			}
1574			if ($this->debug) {
1575				ADOConnection::outp( "CacheFlush: $cmd<br><pre>\n", system($cmd),"</pre>");
1576			} else {
1577				exec($cmd);
1578			}
1579			return;
1580		}
1581
1582		global $ADODB_INCLUDED_CSV;
1583		if (empty($ADODB_INCLUDED_CSV)) include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
1584
1585		$f = $this->_gencachename($sql.serialize($inputarr),false);
1586		adodb_write_file($f,''); // is adodb_write_file needed?
1587		if (!@unlink($f)) {
1588			if ($this->debug) ADOConnection::outp( "CacheFlush: failed for $f");
1589		}
1590	}
1591
1592	/**
1593	* Private function to generate filename for caching.
1594	* Filename is generated based on:
1595	*
1596	*  - sql statement
1597	*  - database type (oci8, ibase, ifx, etc)
1598	*  - database name
1599	*  - userid
1600	*  - setFetchMode (adodb 4.23)
1601	*
1602	* When not in safe mode, we create 256 sub-directories in the cache directory ($ADODB_CACHE_DIR).
1603	* Assuming that we can have 50,000 files per directory with good performance,
1604	* then we can scale to 12.8 million unique cached recordsets. Wow!
1605 	*/
1606	function _gencachename($sql,$createdir)
1607	{
1608	global $ADODB_CACHE_DIR;
1609	static $notSafeMode;
1610
1611		if ($this->fetchMode === false) {
1612		global $ADODB_FETCH_MODE;
1613			$mode = $ADODB_FETCH_MODE;
1614		} else {
1615			$mode = $this->fetchMode;
1616		}
1617		$m = md5($sql.$this->databaseType.$this->database.$this->user.$mode);
1618
1619		if (!isset($notSafeMode)) $notSafeMode = !ini_get('safe_mode');
1620		$dir = ($notSafeMode) ? $ADODB_CACHE_DIR.'/'.substr($m,0,2) : $ADODB_CACHE_DIR;
1621
1622		if ($createdir && $notSafeMode && !file_exists($dir)) {
1623			$oldu = umask(0);
1624			if (!mkdir($dir,0771))
1625				if ($this->debug) ADOConnection::outp( "Unable to mkdir $dir for $sql");
1626			umask($oldu);
1627		}
1628		return $dir.'/adodb_'.$m.'.cache';
1629	}
1630
1631
1632	/**
1633	 * Execute SQL, caching recordsets.
1634	 *
1635	 * @param [secs2cache]	seconds to cache data, set to 0 to force query.
1636	 *					  This is an optional parameter.
1637	 * @param sql		SQL statement to execute
1638	 * @param [inputarr]	holds the input data  to bind to
1639	 * @return 		RecordSet or false
1640	 */
1641	function &CacheExecute($secs2cache,$sql=false,$inputarr=false)
1642	{
1643
1644
1645		if (!is_numeric($secs2cache)) {
1646			$inputarr = $sql;
1647			$sql = $secs2cache;
1648			$secs2cache = $this->cacheSecs;
1649		}
1650
1651		if (is_array($sql)) {
1652			$sqlparam = $sql;
1653			$sql = $sql[0];
1654		} else
1655			$sqlparam = $sql;
1656
1657		global $ADODB_INCLUDED_CSV;
1658		if (empty($ADODB_INCLUDED_CSV)) include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
1659
1660		$md5file = $this->_gencachename($sql.serialize($inputarr),true);
1661		$err = '';
1662
1663		if ($secs2cache > 0){
1664			$rs = &csv2rs($md5file,$err,$secs2cache,$this->arrayClass);
1665			$this->numCacheHits += 1;
1666		} else {
1667			$err='Timeout 1';
1668			$rs = false;
1669			$this->numCacheMisses += 1;
1670		}
1671		if (!$rs) {
1672		// no cached rs found
1673			if ($this->debug) {
1674				if (get_magic_quotes_runtime()) {
1675					ADOConnection::outp("Please disable magic_quotes_runtime - it corrupts cache files :(");
1676				}
1677				if ($this->debug !== -1) ADOConnection::outp( " $md5file cache failure: $err (see sql below)");
1678			}
1679
1680			$rs = &$this->Execute($sqlparam,$inputarr);
1681
1682			if ($rs) {
1683				$eof = $rs->EOF;
1684				$rs = &$this->_rs2rs($rs); // read entire recordset into memory immediately
1685				$txt = _rs2serialize($rs,false,$sql); // serialize
1686
1687				if (!adodb_write_file($md5file,$txt,$this->debug)) {
1688					if ($fn = $this->raiseErrorFn) {
1689						$fn($this->databaseType,'CacheExecute',-32000,"Cache write error",$md5file,$sql,$this);
1690					}
1691					if ($this->debug) ADOConnection::outp( " Cache write error");
1692				}
1693				if ($rs->EOF && !$eof) {
1694					$rs->MoveFirst();
1695					//$rs = &csv2rs($md5file,$err);
1696					$rs->connection = &$this; // Pablo suggestion
1697				}
1698
1699			} else
1700				@unlink($md5file);
1701		} else {
1702			$this->_errorMsg = '';
1703			$this->_errorCode = 0;
1704
1705			if ($this->fnCacheExecute) {
1706				$fn = $this->fnCacheExecute;
1707				$fn($this, $secs2cache, $sql, $inputarr);
1708			}
1709		// ok, set cached object found
1710			$rs->connection = &$this; // Pablo suggestion
1711			if ($this->debug){
1712
1713				$inBrowser = isset($_SERVER['HTTP_USER_AGENT']);
1714				$ttl = $rs->timeCreated + $secs2cache - time();
1715				$s = is_array($sql) ? $sql[0] : $sql;
1716				if ($inBrowser) $s = '<i>'.htmlspecialchars($s).'</i>';
1717
1718				ADOConnection::outp( " $md5file reloaded, ttl=$ttl [ $s ]");
1719			}
1720		}
1721		return $rs;
1722	}
1723
1724
1725	/*
1726		Similar to PEAR DB's autoExecute(), except that
1727		$mode can be 'INSERT' or 'UPDATE' or DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
1728		If $mode == 'UPDATE', then $where is compulsory as a safety measure.
1729
1730		$forceUpdate means that even if the data has not changed, perform update.
1731	 */
1732	function& AutoExecute($table, $fields_values, $mode = 'INSERT', $where = FALSE, $forceUpdate=true, $magicq=false)
1733	{
1734		$sql = 'SELECT * FROM '.$table;
1735		if ($where!==FALSE) $sql .= ' WHERE '.$where;
1736		else if ($mode == 'UPDATE' || $mode == 2 /* DB_AUTOQUERY_UPDATE */) {
1737			ADOConnection::outp('AutoExecute: Illegal mode=UPDATE with empty WHERE clause');
1738			return false;
1739		}
1740
1741		$rs =& $this->SelectLimit($sql,1);
1742		if (!$rs) return false; // table does not exist
1743		$rs->tableName = $table;
1744
1745		switch((string) $mode) {
1746		case 'UPDATE':
1747		case '2':
1748			$sql = $this->GetUpdateSQL($rs, $fields_values, $forceUpdate, $magicq);
1749			break;
1750		case 'INSERT':
1751		case '1':
1752			$sql = $this->GetInsertSQL($rs, $fields_values, $magicq);
1753			break;
1754		default:
1755			ADOConnection::outp("AutoExecute: Unknown mode=$mode");
1756			return false;
1757		}
1758		$ret = false;
1759		if ($sql) $ret = $this->Execute($sql);
1760		if ($ret) $ret = true;
1761		return $ret;
1762	}
1763
1764
1765	/**
1766	 * Generates an Update Query based on an existing recordset.
1767	 * $arrFields is an associative array of fields with the value
1768	 * that should be assigned.
1769	 *
1770	 * Note: This function should only be used on a recordset
1771	 *	   that is run against a single table and sql should only
1772	 *		 be a simple select stmt with no groupby/orderby/limit
1773	 *
1774	 * "Jonathan Younger" <jyounger@unilab.com>
1775  	 */
1776	function GetUpdateSQL(&$rs, $arrFields,$forceUpdate=false,$magicq=false,$force=null)
1777	{
1778		global $ADODB_INCLUDED_LIB;
1779
1780        //********************************************************//
1781        //This is here to maintain compatibility
1782        //with older adodb versions. Sets force type to force nulls if $forcenulls is set.
1783		if (!isset($force)) {
1784				global $ADODB_FORCE_TYPE;
1785			    $force = $ADODB_FORCE_TYPE;
1786		}
1787		//********************************************************//
1788
1789		if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php');
1790		return _adodb_getupdatesql($this,$rs,$arrFields,$forceUpdate,$magicq,$force);
1791	}
1792
1793	/**
1794	 * Generates an Insert Query based on an existing recordset.
1795	 * $arrFields is an associative array of fields with the value
1796	 * that should be assigned.
1797	 *
1798	 * Note: This function should only be used on a recordset
1799	 *	   that is run against a single table.
1800  	 */
1801	function GetInsertSQL(&$rs, $arrFields,$magicq=false,$force=null)
1802	{
1803		global $ADODB_INCLUDED_LIB;
1804		if (!isset($force)) {
1805			global $ADODB_FORCE_TYPE;
1806			$force = $ADODB_FORCE_TYPE;
1807
1808		}
1809		if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php');
1810		return _adodb_getinsertsql($this,$rs,$arrFields,$magicq,$force);
1811	}
1812
1813
1814	/**
1815	* Update a blob column, given a where clause. There are more sophisticated
1816	* blob handling functions that we could have implemented, but all require
1817	* a very complex API. Instead we have chosen something that is extremely
1818	* simple to understand and use.
1819	*
1820	* Note: $blobtype supports 'BLOB' and 'CLOB', default is BLOB of course.
1821	*
1822	* Usage to update a $blobvalue which has a primary key blob_id=1 into a
1823	* field blobtable.blobcolumn:
1824	*
1825	*	UpdateBlob('blobtable', 'blobcolumn', $blobvalue, 'blob_id=1');
1826	*
1827	* Insert example:
1828	*
1829	*	$conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
1830	*	$conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
1831	*/
1832
1833	function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
1834	{
1835		return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false;
1836	}
1837
1838	/**
1839	* Usage:
1840	*	UpdateBlob('TABLE', 'COLUMN', '/path/to/file', 'ID=1');
1841	*
1842	*	$blobtype supports 'BLOB' and 'CLOB'
1843	*
1844	*	$conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
1845	*	$conn->UpdateBlob('blobtable','blobcol',$blobpath,'id=1');
1846	*/
1847	function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB')
1848	{
1849		$fd = fopen($path,'rb');
1850		if ($fd === false) return false;
1851		$val = fread($fd,filesize($path));
1852		fclose($fd);
1853		return $this->UpdateBlob($table,$column,$val,$where,$blobtype);
1854	}
1855
1856	function BlobDecode($blob)
1857	{
1858		return $blob;
1859	}
1860
1861	function BlobEncode($blob)
1862	{
1863		return $blob;
1864	}
1865
1866	function SetCharSet($charset)
1867	{
1868		return false;
1869	}
1870
1871	function IfNull( $field, $ifNull )
1872	{
1873		return " CASE WHEN $field is null THEN $ifNull ELSE $field END ";
1874	}
1875
1876	function LogSQL($enable=true)
1877	{
1878		include_once(ADODB_DIR.'/adodb-perf.inc.php');
1879
1880		if ($enable) $this->fnExecute = 'adodb_log_sql';
1881		else $this->fnExecute = false;
1882
1883		$old = $this->_logsql;
1884		$this->_logsql = $enable;
1885		if ($enable && !$old) $this->_affected = false;
1886		return $old;
1887	}
1888
1889	function GetCharSet()
1890	{
1891		return false;
1892	}
1893
1894	/**
1895	* Usage:
1896	*	UpdateClob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB');
1897	*
1898	*	$conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)');
1899	*	$conn->UpdateClob('clobtable','clobcol',$clob,'id=1');
1900	*/
1901	function UpdateClob($table,$column,$val,$where)
1902	{
1903		return $this->UpdateBlob($table,$column,$val,$where,'CLOB');
1904	}
1905
1906	// not the fastest implementation - quick and dirty - jlim
1907	// for best performance, use the actual $rs->MetaType().
1908	function MetaType($t,$len=-1,$fieldobj=false)
1909	{
1910
1911		if (empty($this->_metars)) {
1912			$rsclass = $this->rsPrefix.$this->databaseType;
1913			$this->_metars =& new $rsclass(false,$this->fetchMode);
1914			$this->_metars->connection =& $this;
1915		}
1916		return $this->_metars->MetaType($t,$len,$fieldobj);
1917	}
1918
1919
1920	/**
1921	*  Change the SQL connection locale to a specified locale.
1922	*  This is used to get the date formats written depending on the client locale.
1923	*/
1924	function SetDateLocale($locale = 'En')
1925	{
1926		$this->locale = $locale;
1927		switch (strtoupper($locale))
1928		{
1929			case 'EN':
1930				$this->fmtDate="'Y-m-d'";
1931				$this->fmtTimeStamp = "'Y-m-d H:i:s'";
1932				break;
1933
1934			case 'US':
1935				$this->fmtDate = "'m-d-Y'";
1936				$this->fmtTimeStamp = "'m-d-Y H:i:s'";
1937				break;
1938
1939			case 'NL':
1940			case 'FR':
1941			case 'RO':
1942			case 'IT':
1943				$this->fmtDate="'d-m-Y'";
1944				$this->fmtTimeStamp = "'d-m-Y H:i:s'";
1945				break;
1946
1947			case 'GE':
1948				$this->fmtDate="'d.m.Y'";
1949				$this->fmtTimeStamp = "'d.m.Y H:i:s'";
1950				break;
1951
1952			default:
1953				$this->fmtDate="'Y-m-d'";
1954				$this->fmtTimeStamp = "'Y-m-d H:i:s'";
1955				break;
1956		}
1957	}
1958
1959	function &GetActiveRecordsClass($class, $table,$whereOrderBy=false,$bindarr=false, $primkeyArr=false)
1960	{
1961	global $_ADODB_ACTIVE_DBS;
1962
1963		$save = $this->SetFetchMode(ADODB_FETCH_NUM);
1964		if (empty($whereOrderBy)) $whereOrderBy = '1=1';
1965		$rows = $this->GetAll("select * from ".$table.' WHERE '.$whereOrderBy,$bindarr);
1966		$this->SetFetchMode($save);
1967
1968		$false = false;
1969
1970		if ($rows === false) {
1971			return $false;
1972		}
1973
1974
1975		if (!isset($_ADODB_ACTIVE_DBS)) {
1976			include_once(ADODB_DIR.'/adodb-active-record.inc.php');
1977		}
1978		if (!class_exists($class)) {
1979			ADOConnection::outp("Unknown class $class in GetActiveRcordsClass()");
1980			return $false;
1981		}
1982		$arr = array();
1983		foreach($rows as $row) {
1984
1985			$obj =& new $class($table,$primkeyArr,$this);
1986			if ($obj->ErrorMsg()){
1987				$this->_errorMsg = $obj->ErrorMsg();
1988				return $false;
1989			}
1990			$obj->Set($row);
1991			$arr[] =& $obj;
1992		}
1993		return $arr;
1994	}
1995
1996	function &GetActiveRecords($table,$where=false,$bindarr=false,$primkeyArr=false)
1997	{
1998		$arr =& $this->GetActiveRecordsClass('ADODB_Active_Record', $table, $where, $bindarr, $primkeyArr);
1999		return $arr;
2000	}
2001
2002	/**
2003	 * Close Connection
2004	 */
2005	function Close()
2006	{
2007		$rez = $this->_close();
2008		$this->_connectionID = false;
2009		return $rez;
2010	}
2011
2012	/**
2013	 * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans().
2014	 *
2015	 * @return true if succeeded or false if database does not support transactions
2016	 */
2017	function BeginTrans() {return false;}
2018
2019
2020	/**
2021	 * If database does not support transactions, always return true as data always commited
2022	 *
2023	 * @param $ok  set to false to rollback transaction, true to commit
2024	 *
2025	 * @return true/false.
2026	 */
2027	function CommitTrans($ok=true)
2028	{ return true;}
2029
2030
2031	/**
2032	 * If database does not support transactions, rollbacks always fail, so return false
2033	 *
2034	 * @return true/false.
2035	 */
2036	function RollbackTrans()
2037	{ return false;}
2038
2039
2040	/**
2041	 * return the databases that the driver can connect to.
2042	 * Some databases will return an empty array.
2043	 *
2044	 * @return an array of database names.
2045	 */
2046		function MetaDatabases()
2047		{
2048		global $ADODB_FETCH_MODE;
2049
2050			if ($this->metaDatabasesSQL) {
2051				$save = $ADODB_FETCH_MODE;
2052				$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2053
2054				if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
2055
2056				$arr = $this->GetCol($this->metaDatabasesSQL);
2057				if (isset($savem)) $this->SetFetchMode($savem);
2058				$ADODB_FETCH_MODE = $save;
2059
2060				return $arr;
2061			}
2062
2063			return false;
2064		}
2065
2066
2067	/**
2068	 * @param ttype can either be 'VIEW' or 'TABLE' or false.
2069	 * 		If false, both views and tables are returned.
2070	 *		"VIEW" returns only views
2071	 *		"TABLE" returns only tables
2072	 * @param showSchema returns the schema/user with the table name, eg. USER.TABLE
2073	 * @param mask  is the input mask - only supported by oci8 and postgresql
2074	 *
2075	 * @return  array of tables for current database.
2076	 */
2077	function &MetaTables($ttype=false,$showSchema=false,$mask=false)
2078	{
2079	global $ADODB_FETCH_MODE;
2080
2081
2082		$false = false;
2083		if ($mask) {
2084			return $false;
2085		}
2086		if ($this->metaTablesSQL) {
2087			$save = $ADODB_FETCH_MODE;
2088			$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2089
2090			if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
2091
2092			$rs = $this->Execute($this->metaTablesSQL);
2093			if (isset($savem)) $this->SetFetchMode($savem);
2094			$ADODB_FETCH_MODE = $save;
2095
2096			if ($rs === false) return $false;
2097			$arr =& $rs->GetArray();
2098			$arr2 = array();
2099
2100			if ($hast = ($ttype && isset($arr[0][1]))) {
2101				$showt = strncmp($ttype,'T',1);
2102			}
2103
2104			for ($i=0; $i < sizeof($arr); $i++) {
2105				if ($hast) {
2106					if ($showt == 0) {
2107						if (strncmp($arr[$i][1],'T',1) == 0) $arr2[] = trim($arr[$i][0]);
2108					} else {
2109						if (strncmp($arr[$i][1],'V',1) == 0) $arr2[] = trim($arr[$i][0]);
2110					}
2111				} else
2112					$arr2[] = trim($arr[$i][0]);
2113			}
2114			$rs->Close();
2115			return $arr2;
2116		}
2117		return $false;
2118	}
2119
2120
2121	function _findschema(&$table,&$schema)
2122	{
2123		if (!$schema && ($at = strpos($table,'.')) !== false) {
2124			$schema = substr($table,0,$at);
2125			$table = substr($table,$at+1);
2126		}
2127	}
2128
2129	/**
2130	 * List columns in a database as an array of ADOFieldObjects.
2131	 * See top of file for definition of object.
2132	 *
2133	 * @param $table	table name to query
2134	 * @param $normalize	makes table name case-insensitive (required by some databases)
2135	 * @schema is optional database schema to use - not supported by all databases.
2136	 *
2137	 * @return  array of ADOFieldObjects for current table.
2138	 */
2139	function &MetaColumns($table,$normalize=true)
2140	{
2141	global $ADODB_FETCH_MODE;
2142
2143		$false = false;
2144
2145		if (!empty($this->metaColumnsSQL)) {
2146
2147			$schema = false;
2148			$this->_findschema($table,$schema);
2149
2150			$save = $ADODB_FETCH_MODE;
2151			$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2152			if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
2153			$rs = $this->Execute(sprintf($this->metaColumnsSQL,($normalize)?strtoupper($table):$table));
2154			if (isset($savem)) $this->SetFetchMode($savem);
2155			$ADODB_FETCH_MODE = $save;
2156			if ($rs === false || $rs->EOF) return $false;
2157
2158			$retarr = array();
2159			while (!$rs->EOF) { //print_r($rs->fields);
2160				$fld = new ADOFieldObject();
2161				$fld->name = $rs->fields[0];
2162				$fld->type = $rs->fields[1];
2163				if (isset($rs->fields[3]) && $rs->fields[3]) {
2164					if ($rs->fields[3]>0) $fld->max_length = $rs->fields[3];
2165					$fld->scale = $rs->fields[4];
2166					if ($fld->scale>0) $fld->max_length += 1;
2167				} else
2168					$fld->max_length = $rs->fields[2];
2169
2170				if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld;
2171				else $retarr[strtoupper($fld->name)] = $fld;
2172				$rs->MoveNext();
2173			}
2174			$rs->Close();
2175			return $retarr;
2176		}
2177		return $false;
2178	}
2179
2180    /**
2181      * List indexes on a table as an array.
2182      * @param table  table name to query
2183      * @param primary true to only show primary keys. Not actually used for most databases
2184	  *
2185      * @return array of indexes on current table. Each element represents an index, and is itself an associative array.
2186
2187		 Array (
2188		    [name_of_index] => Array
2189		      (
2190	          [unique] => true or false
2191	          [columns] => Array
2192	          (
2193	          	[0] => firstname
2194		      	[1] => lastname
2195	          )
2196		)
2197      */
2198     function &MetaIndexes($table, $primary = false, $owner = false)
2199     {
2200	 		$false = false;
2201            return $false;
2202     }
2203
2204	/**
2205	 * List columns names in a table as an array.
2206	 * @param table	table name to query
2207	 *
2208	 * @return  array of column names for current table.
2209	 */
2210	function &MetaColumnNames($table, $numIndexes=false)
2211	{
2212		$objarr =& $this->MetaColumns($table);
2213		if (!is_array($objarr)) {
2214			$false = false;
2215			return $false;
2216		}
2217		$arr = array();
2218		if ($numIndexes) {
2219			$i = 0;
2220			foreach($objarr as $v) $arr[$i++] = $v->name;
2221		} else
2222			foreach($objarr as $v) $arr[strtoupper($v->name)] = $v->name;
2223
2224		return $arr;
2225	}
2226
2227	/**
2228	 * Different SQL databases used different methods to combine strings together.
2229	 * This function provides a wrapper.
2230	 *
2231	 * param s	variable number of string parameters
2232	 *
2233	 * Usage: $db->Concat($str1,$str2);
2234	 *
2235	 * @return concatenated string
2236	 */
2237	function Concat()
2238	{
2239		$arr = func_get_args();
2240		return implode($this->concat_operator, $arr);
2241	}
2242
2243
2244	/**
2245	 * Converts a date "d" to a string that the database can understand.
2246	 *
2247	 * @param d	a date in Unix date time format.
2248	 *
2249	 * @return  date string in database date format
2250	 */
2251	function DBDate($d)
2252	{
2253		if (empty($d) && $d !== 0) return 'null';
2254
2255		if (is_string($d) && !is_numeric($d)) {
2256			if ($d === 'null' || strncmp($d,"'",1) === 0) return $d;
2257			if ($this->isoDates) return "'$d'";
2258			$d = ADOConnection::UnixDate($d);
2259		}
2260
2261		return adodb_date($this->fmtDate,$d);
2262	}
2263
2264
2265	/**
2266	 * Converts a timestamp "ts" to a string that the database can understand.
2267	 *
2268	 * @param ts	a timestamp in Unix date time format.
2269	 *
2270	 * @return  timestamp string in database timestamp format
2271	 */
2272	function DBTimeStamp($ts)
2273	{
2274		if (empty($ts) && $ts !== 0) return 'null';
2275
2276		# strlen(14) allows YYYYMMDDHHMMSS format
2277		if (!is_string($ts) || (is_numeric($ts) && strlen($ts)<14))
2278			return adodb_date($this->fmtTimeStamp,$ts);
2279
2280		if ($ts === 'null') return $ts;
2281		if ($this->isoDates && strlen($ts) !== 14) return "'$ts'";
2282
2283		$ts = ADOConnection::UnixTimeStamp($ts);
2284		return adodb_date($this->fmtTimeStamp,$ts);
2285	}
2286
2287	/**
2288	 * Also in ADORecordSet.
2289	 * @param $v is a date string in YYYY-MM-DD format
2290	 *
2291	 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2292	 */
2293	function UnixDate($v)
2294	{
2295		if (is_object($v)) {
2296		// odbtp support
2297		//( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
2298			return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
2299		}
2300
2301		if (is_numeric($v) && strlen($v) !== 8) return $v;
2302		if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|",
2303			($v), $rr)) return false;
2304
2305		if ($rr[1] <= TIMESTAMP_FIRST_YEAR) return 0;
2306		// h-m-s-MM-DD-YY
2307		return @adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
2308	}
2309
2310
2311	/**
2312	 * Also in ADORecordSet.
2313	 * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
2314	 *
2315	 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2316	 */
2317	function UnixTimeStamp($v)
2318	{
2319		if (is_object($v)) {
2320		// odbtp support
2321		//( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
2322			return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
2323		}
2324
2325		if (!preg_match(
2326			"|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ ,-]*(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|",
2327			($v), $rr)) return false;
2328
2329		if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) return 0;
2330
2331		// h-m-s-MM-DD-YY
2332		if (!isset($rr[5])) return  adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
2333		return  @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]);
2334	}
2335
2336	/**
2337	 * Also in ADORecordSet.
2338	 *
2339	 * Format database date based on user defined format.
2340	 *
2341	 * @param v  	is the character date in YYYY-MM-DD format, returned by database
2342	 * @param fmt 	is the format to apply to it, using date()
2343	 *
2344	 * @return a date formated as user desires
2345	 */
2346
2347	function UserDate($v,$fmt='Y-m-d',$gmt=false)
2348	{
2349		$tt = $this->UnixDate($v);
2350
2351		// $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2352		if (($tt === false || $tt == -1) && $v != false) return $v;
2353		else if ($tt == 0) return $this->emptyDate;
2354		else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR
2355		}
2356
2357		return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
2358
2359	}
2360
2361		/**
2362	 *
2363	 * @param v  	is the character timestamp in YYYY-MM-DD hh:mm:ss format
2364	 * @param fmt 	is the format to apply to it, using date()
2365	 *
2366	 * @return a timestamp formated as user desires
2367	 */
2368	function UserTimeStamp($v,$fmt='Y-m-d H:i:s',$gmt=false)
2369	{
2370		if (!isset($v)) return $this->emptyTimeStamp;
2371		# strlen(14) allows YYYYMMDDHHMMSS format
2372		if (is_numeric($v) && strlen($v)<14) return ($gmt) ? adodb_gmdate($fmt,$v) : adodb_date($fmt,$v);
2373		$tt = $this->UnixTimeStamp($v);
2374		// $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2375		if (($tt === false || $tt == -1) && $v != false) return $v;
2376		if ($tt == 0) return $this->emptyTimeStamp;
2377		return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
2378	}
2379
2380	function escape($s,$magic_quotes=false)
2381	{
2382		return $this->addq($s,$magic_quotes);
2383	}
2384
2385	/**
2386	* Quotes a string, without prefixing nor appending quotes.
2387	*/
2388	function addq($s,$magic_quotes=false)
2389	{
2390		if (!$magic_quotes) {
2391
2392			if ($this->replaceQuote[0] == '\\'){
2393				// only since php 4.0.5
2394				$s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
2395				//$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
2396			}
2397			return  str_replace("'",$this->replaceQuote,$s);
2398		}
2399
2400		// undo magic quotes for "
2401		$s = str_replace('\\"','"',$s);
2402
2403		if ($this->replaceQuote == "\\'")  // ' already quoted, no need to change anything
2404			return $s;
2405		else {// change \' to '' for sybase/mssql
2406			$s = str_replace('\\\\','\\',$s);
2407			return str_replace("\\'",$this->replaceQuote,$s);
2408		}
2409	}
2410
2411	/**
2412	 * Correctly quotes a string so that all strings are escaped. We prefix and append
2413	 * to the string single-quotes.
2414	 * An example is  $db->qstr("Don't bother",magic_quotes_runtime());
2415	 *
2416	 * @param s			the string to quote
2417	 * @param [magic_quotes]	if $s is GET/POST var, set to get_magic_quotes_gpc().
2418	 *				This undoes the stupidity of magic quotes for GPC.
2419	 *
2420	 * @return  quoted string to be sent back to database
2421	 */
2422	function qstr($s,$magic_quotes=false)
2423	{
2424		if (!$magic_quotes) {
2425
2426			if ($this->replaceQuote[0] == '\\'){
2427				// only since php 4.0.5
2428				$s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
2429				//$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
2430			}
2431			return  "'".str_replace("'",$this->replaceQuote,$s)."'";
2432		}
2433
2434		// undo magic quotes for "
2435		$s = str_replace('\\"','"',$s);
2436
2437		if ($this->replaceQuote == "\\'")  // ' already quoted, no need to change anything
2438			return "'$s'";
2439		else {// change \' to '' for sybase/mssql
2440			$s = str_replace('\\\\','\\',$s);
2441			return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
2442		}
2443	}
2444
2445
2446	/**
2447	* Will select the supplied $page number from a recordset, given that it is paginated in pages of
2448	* $nrows rows per page. It also saves two boolean values saying if the given page is the first
2449	* and/or last one of the recordset. Added by Iv�n Oliva to provide recordset pagination.
2450	*
2451	* See readme.htm#ex8 for an example of usage.
2452	*
2453	* @param sql
2454	* @param nrows		is the number of rows per page to get
2455	* @param page		is the page number to get (1-based)
2456	* @param [inputarr]	array of bind variables
2457	* @param [secs2cache]		is a private parameter only used by jlim
2458	* @return		the recordset ($rs->databaseType == 'array')
2459	*
2460	* NOTE: phpLens uses a different algorithm and does not use PageExecute().
2461	*
2462	*/
2463	function &PageExecute($sql, $nrows, $page, $inputarr=false, $secs2cache=0)
2464	{
2465		global $ADODB_INCLUDED_LIB;
2466		if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php');
2467		if ($this->pageExecuteCountRows) $rs =& _adodb_pageexecute_all_rows($this, $sql, $nrows, $page, $inputarr, $secs2cache);
2468		else $rs =& _adodb_pageexecute_no_last_page($this, $sql, $nrows, $page, $inputarr, $secs2cache);
2469		return $rs;
2470	}
2471
2472
2473	/**
2474	* Will select the supplied $page number from a recordset, given that it is paginated in pages of
2475	* $nrows rows per page. It also saves two boolean values saying if the given page is the first
2476	* and/or last one of the recordset. Added by Iv�n Oliva to provide recordset pagination.
2477	*
2478	* @param secs2cache	seconds to cache data, set to 0 to force query
2479	* @param sql
2480	* @param nrows		is the number of rows per page to get
2481	* @param page		is the page number to get (1-based)
2482	* @param [inputarr]	array of bind variables
2483	* @return		the recordset ($rs->databaseType == 'array')
2484	*/
2485	function &CachePageExecute($secs2cache, $sql, $nrows, $page,$inputarr=false)
2486	{
2487		/*switch($this->dataProvider) {
2488		case 'postgres':
2489		case 'mysql':
2490			break;
2491		default: $secs2cache = 0; break;
2492		}*/
2493		$rs =& $this->PageExecute($sql,$nrows,$page,$inputarr,$secs2cache);
2494		return $rs;
2495	}
2496
2497} // end class ADOConnection
2498
2499
2500
2501	//==============================================================================================
2502	// CLASS ADOFetchObj
2503	//==============================================================================================
2504
2505	/**
2506	* Internal placeholder for record objects. Used by ADORecordSet->FetchObj().
2507	*/
2508	class ADOFetchObj {
2509	};
2510
2511	//==============================================================================================
2512	// CLASS ADORecordSet_empty
2513	//==============================================================================================
2514
2515	/**
2516	* Lightweight recordset when there are no records to be returned
2517	*/
2518	class ADORecordSet_empty
2519	{
2520		var $dataProvider = 'empty';
2521		var $databaseType = false;
2522		var $EOF = true;
2523		var $_numOfRows = 0;
2524		var $fields = false;
2525		var $connection = false;
2526		function RowCount() {return 0;}
2527		function RecordCount() {return 0;}
2528		function PO_RecordCount(){return 0;}
2529		function Close(){return true;}
2530		function FetchRow() {return false;}
2531		function FieldCount(){ return 0;}
2532		function Init() {}
2533	}
2534
2535	//==============================================================================================
2536	// DATE AND TIME FUNCTIONS
2537	//==============================================================================================
2538	include_once(ADODB_DIR.'/adodb-time.inc.php');
2539
2540	//==============================================================================================
2541	// CLASS ADORecordSet
2542	//==============================================================================================
2543
2544	if (PHP_VERSION < 5) include_once(ADODB_DIR.'/adodb-php4.inc.php');
2545	else include_once(ADODB_DIR.'/adodb-iterator.inc.php');
2546   /**
2547	 * RecordSet class that represents the dataset returned by the database.
2548	 * To keep memory overhead low, this class holds only the current row in memory.
2549	 * No prefetching of data is done, so the RecordCount() can return -1 ( which
2550	 * means recordcount not known).
2551	 */
2552	class ADORecordSet extends ADODB_BASE_RS {
2553	/*
2554	 * public variables
2555	 */
2556	var $dataProvider = "native";
2557	var $fields = false; 	/// holds the current row data
2558	var $blobSize = 100; 	/// any varchar/char field this size or greater is treated as a blob
2559							/// in other words, we use a text area for editing.
2560	var $canSeek = false; 	/// indicates that seek is supported
2561	var $sql; 				/// sql text
2562	var $EOF = false;		/// Indicates that the current record position is after the last record in a Recordset object.
2563
2564	var $emptyTimeStamp = '&nbsp;'; /// what to display when $time==0
2565	var $emptyDate = '&nbsp;'; /// what to display when $time==0
2566	var $debug = false;
2567	var $timeCreated=0; 	/// datetime in Unix format rs created -- for cached recordsets
2568
2569	var $bind = false; 		/// used by Fields() to hold array - should be private?
2570	var $fetchMode;			/// default fetch mode
2571	var $connection = false; /// the parent connection
2572	/*
2573	 *	private variables
2574	 */
2575	var $_numOfRows = -1;	/** number of rows, or -1 */
2576	var $_numOfFields = -1;	/** number of fields in recordset */
2577	var $_queryID = -1;		/** This variable keeps the result link identifier.	*/
2578	var $_currentRow = -1;	/** This variable keeps the current row in the Recordset.	*/
2579	var $_closed = false; 	/** has recordset been closed */
2580	var $_inited = false; 	/** Init() should only be called once */
2581	var $_obj; 				/** Used by FetchObj */
2582	var $_names;			/** Used by FetchObj */
2583
2584	var $_currentPage = -1;	/** Added by Iv�n Oliva to implement recordset pagination */
2585	var $_atFirstPage = false;	/** Added by Iv�n Oliva to implement recordset pagination */
2586	var $_atLastPage = false;	/** Added by Iv�n Oliva to implement recordset pagination */
2587	var $_lastPageNo = -1;
2588	var $_maxRecordCount = 0;
2589	var $datetime = false;
2590
2591	/**
2592	 * Constructor
2593	 *
2594	 * @param queryID  	this is the queryID returned by ADOConnection->_query()
2595	 *
2596	 */
2597	function ADORecordSet($queryID)
2598	{
2599		$this->_queryID = $queryID;
2600	}
2601
2602
2603
2604	function Init()
2605	{
2606		if ($this->_inited) return;
2607		$this->_inited = true;
2608		if ($this->_queryID) @$this->_initrs();
2609		else {
2610			$this->_numOfRows = 0;
2611			$this->_numOfFields = 0;
2612		}
2613		if ($this->_numOfRows != 0 && $this->_numOfFields && $this->_currentRow == -1) {
2614
2615			$this->_currentRow = 0;
2616			if ($this->EOF = ($this->_fetch() === false)) {
2617				$this->_numOfRows = 0; // _numOfRows could be -1
2618			}
2619		} else {
2620			$this->EOF = true;
2621		}
2622	}
2623
2624
2625	/**
2626	 * Generate a SELECT tag string from a recordset, and return the string.
2627	 * If the recordset has 2 cols, we treat the 1st col as the containing
2628	 * the text to display to the user, and 2nd col as the return value. Default
2629	 * strings are compared with the FIRST column.
2630	 *
2631	 * @param name  		name of SELECT tag
2632	 * @param [defstr]		the value to hilite. Use an array for multiple hilites for listbox.
2633	 * @param [blank1stItem]	true to leave the 1st item in list empty
2634	 * @param [multiple]		true for listbox, false for popup
2635	 * @param [size]		#rows to show for listbox. not used by popup
2636	 * @param [selectAttr]		additional attributes to defined for SELECT tag.
2637	 *				useful for holding javascript onChange='...' handlers.
2638	 & @param [compareFields0]	when we have 2 cols in recordset, we compare the defstr with
2639	 *				column 0 (1st col) if this is true. This is not documented.
2640	 *
2641	 * @return HTML
2642	 *
2643	 * changes by glen.davies@cce.ac.nz to support multiple hilited items
2644	 */
2645	function GetMenu($name,$defstr='',$blank1stItem=true,$multiple=false,
2646			$size=0, $selectAttr='',$compareFields0=true)
2647	{
2648		global $ADODB_INCLUDED_LIB;
2649		if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php');
2650		return _adodb_getmenu($this, $name,$defstr,$blank1stItem,$multiple,
2651			$size, $selectAttr,$compareFields0);
2652	}
2653
2654
2655
2656	/**
2657	 * Generate a SELECT tag string from a recordset, and return the string.
2658	 * If the recordset has 2 cols, we treat the 1st col as the containing
2659	 * the text to display to the user, and 2nd col as the return value. Default
2660	 * strings are compared with the SECOND column.
2661	 *
2662	 */
2663	function GetMenu2($name,$defstr='',$blank1stItem=true,$multiple=false,$size=0, $selectAttr='')
2664	{
2665		return $this->GetMenu($name,$defstr,$blank1stItem,$multiple,
2666			$size, $selectAttr,false);
2667	}
2668
2669	/*
2670		Grouped Menu
2671	*/
2672	function GetMenu3($name,$defstr='',$blank1stItem=true,$multiple=false,
2673			$size=0, $selectAttr='')
2674	{
2675		global $ADODB_INCLUDED_LIB;
2676		if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php');
2677		return _adodb_getmenu_gp($this, $name,$defstr,$blank1stItem,$multiple,
2678			$size, $selectAttr,false);
2679	}
2680
2681	/**
2682	 * return recordset as a 2-dimensional array.
2683	 *
2684	 * @param [nRows]  is the number of rows to return. -1 means every row.
2685	 *
2686	 * @return an array indexed by the rows (0-based) from the recordset
2687	 */
2688	function &GetArray($nRows = -1)
2689	{
2690	global $ADODB_EXTENSION; if ($ADODB_EXTENSION) {
2691		$results = adodb_getall($this,$nRows);
2692		return $results;
2693	}
2694		$results = array();
2695		$cnt = 0;
2696		while (!$this->EOF && $nRows != $cnt) {
2697			$results[] = $this->fields;
2698			$this->MoveNext();
2699			$cnt++;
2700		}
2701		return $results;
2702	}
2703
2704	function &GetAll($nRows = -1)
2705	{
2706		$arr =& $this->GetArray($nRows);
2707		return $arr;
2708	}
2709
2710	/*
2711	* Some databases allow multiple recordsets to be returned. This function
2712	* will return true if there is a next recordset, or false if no more.
2713	*/
2714	function NextRecordSet()
2715	{
2716		return false;
2717	}
2718
2719	/**
2720	 * return recordset as a 2-dimensional array.
2721	 * Helper function for ADOConnection->SelectLimit()
2722	 *
2723	 * @param offset	is the row to start calculations from (1-based)
2724	 * @param [nrows]	is the number of rows to return
2725	 *
2726	 * @return an array indexed by the rows (0-based) from the recordset
2727	 */
2728	function &GetArrayLimit($nrows,$offset=-1)
2729	{
2730		if ($offset <= 0) {
2731			$arr =& $this->GetArray($nrows);
2732			return $arr;
2733		}
2734
2735		$this->Move($offset);
2736
2737		$results = array();
2738		$cnt = 0;
2739		while (!$this->EOF && $nrows != $cnt) {
2740			$results[$cnt++] = $this->fields;
2741			$this->MoveNext();
2742		}
2743
2744		return $results;
2745	}
2746
2747
2748	/**
2749	 * Synonym for GetArray() for compatibility with ADO.
2750	 *
2751	 * @param [nRows]  is the number of rows to return. -1 means every row.
2752	 *
2753	 * @return an array indexed by the rows (0-based) from the recordset
2754	 */
2755	function &GetRows($nRows = -1)
2756	{
2757		$arr =& $this->GetArray($nRows);
2758		return $arr;
2759	}
2760
2761	/**
2762	 * return whole recordset as a 2-dimensional associative array if there are more than 2 columns.
2763	 * The first column is treated as the key and is not included in the array.
2764	 * If there is only 2 columns, it will return a 1 dimensional array of key-value pairs unless
2765	 * $force_array == true.
2766	 *
2767	 * @param [force_array] has only meaning if we have 2 data columns. If false, a 1 dimensional
2768	 * 	array is returned, otherwise a 2 dimensional array is returned. If this sounds confusing,
2769	 * 	read the source.
2770	 *
2771	 * @param [first2cols] means if there are more than 2 cols, ignore the remaining cols and
2772	 * instead of returning array[col0] => array(remaining cols), return array[col0] => col1
2773	 *
2774	 * @return an associative array indexed by the first column of the array,
2775	 * 	or false if the  data has less than 2 cols.
2776	 */
2777	function &GetAssoc($force_array = false, $first2cols = false)
2778	{
2779	global $ADODB_EXTENSION;
2780
2781		$cols = $this->_numOfFields;
2782		if ($cols < 2) {
2783			$false = false;
2784			return $false;
2785		}
2786		$numIndex = isset($this->fields[0]);
2787		$results = array();
2788
2789		if (!$first2cols && ($cols > 2 || $force_array)) {
2790			if ($ADODB_EXTENSION) {
2791				if ($numIndex) {
2792					while (!$this->EOF) {
2793						$results[trim($this->fields[0])] = array_slice($this->fields, 1);
2794						adodb_movenext($this);
2795					}
2796				} else {
2797					while (!$this->EOF) {
2798						$results[trim(reset($this->fields))] = array_slice($this->fields, 1);
2799						adodb_movenext($this);
2800					}
2801				}
2802			} else {
2803				if ($numIndex) {
2804					while (!$this->EOF) {
2805						$results[trim($this->fields[0])] = array_slice($this->fields, 1);
2806						$this->MoveNext();
2807					}
2808				} else {
2809					while (!$this->EOF) {
2810						$results[trim(reset($this->fields))] = array_slice($this->fields, 1);
2811						$this->MoveNext();
2812					}
2813				}
2814			}
2815		} else {
2816			if ($ADODB_EXTENSION) {
2817				// return scalar values
2818				if ($numIndex) {
2819					while (!$this->EOF) {
2820					// some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
2821						$results[trim(($this->fields[0]))] = $this->fields[1];
2822						adodb_movenext($this);
2823					}
2824				} else {
2825					while (!$this->EOF) {
2826					// some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
2827						$v1 = trim(reset($this->fields));
2828						$v2 = ''.next($this->fields);
2829						$results[$v1] = $v2;
2830						adodb_movenext($this);
2831					}
2832				}
2833			} else {
2834				if ($numIndex) {
2835					while (!$this->EOF) {
2836					// some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
2837						$results[trim(($this->fields[0]))] = $this->fields[1];
2838						$this->MoveNext();
2839					}
2840				} else {
2841					while (!$this->EOF) {
2842					// some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
2843						$v1 = trim(reset($this->fields));
2844						$v2 = ''.next($this->fields);
2845						$results[$v1] = $v2;
2846						$this->MoveNext();
2847					}
2848				}
2849			}
2850		}
2851
2852		$ref =& $results; # workaround accelerator incompat with PHP 4.4 :(
2853		return $ref;
2854	}
2855
2856
2857	/**
2858	 *
2859	 * @param v  	is the character timestamp in YYYY-MM-DD hh:mm:ss format
2860	 * @param fmt 	is the format to apply to it, using date()
2861	 *
2862	 * @return a timestamp formated as user desires
2863	 */
2864	function UserTimeStamp($v,$fmt='Y-m-d H:i:s')
2865	{
2866		if (is_numeric($v) && strlen($v)<14) return adodb_date($fmt,$v);
2867		$tt = $this->UnixTimeStamp($v);
2868		// $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2869		if (($tt === false || $tt == -1) && $v != false) return $v;
2870		if ($tt === 0) return $this->emptyTimeStamp;
2871		return adodb_date($fmt,$tt);
2872	}
2873
2874
2875	/**
2876	 * @param v  	is the character date in YYYY-MM-DD format, returned by database
2877	 * @param fmt 	is the format to apply to it, using date()
2878	 *
2879	 * @return a date formated as user desires
2880	 */
2881	function UserDate($v,$fmt='Y-m-d')
2882	{
2883		$tt = $this->UnixDate($v);
2884		// $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2885		if (($tt === false || $tt == -1) && $v != false) return $v;
2886		else if ($tt == 0) return $this->emptyDate;
2887		else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR
2888		}
2889		return adodb_date($fmt,$tt);
2890	}
2891
2892
2893	/**
2894	 * @param $v is a date string in YYYY-MM-DD format
2895	 *
2896	 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2897	 */
2898	function UnixDate($v)
2899	{
2900		return ADOConnection::UnixDate($v);
2901	}
2902
2903
2904	/**
2905	 * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
2906	 *
2907	 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2908	 */
2909	function UnixTimeStamp($v)
2910	{
2911		return ADOConnection::UnixTimeStamp($v);
2912	}
2913
2914
2915	/**
2916	* PEAR DB Compat - do not use internally
2917	*/
2918	function Free()
2919	{
2920		return $this->Close();
2921	}
2922
2923
2924	/**
2925	* PEAR DB compat, number of rows
2926	*/
2927	function NumRows()
2928	{
2929		return $this->_numOfRows;
2930	}
2931
2932
2933	/**
2934	* PEAR DB compat, number of cols
2935	*/
2936	function NumCols()
2937	{
2938		return $this->_numOfFields;
2939	}
2940
2941	/**
2942	* Fetch a row, returning false if no more rows.
2943	* This is PEAR DB compat mode.
2944	*
2945	* @return false or array containing the current record
2946	*/
2947	function &FetchRow()
2948	{
2949		if ($this->EOF) {
2950			$false = false;
2951			return $false;
2952		}
2953		$arr = $this->fields;
2954		$this->_currentRow++;
2955		if (!$this->_fetch()) $this->EOF = true;
2956		return $arr;
2957	}
2958
2959
2960	/**
2961	* Fetch a row, returning PEAR_Error if no more rows.
2962	* This is PEAR DB compat mode.
2963	*
2964	* @return DB_OK or error object
2965	*/
2966	function FetchInto(&$arr)
2967	{
2968		if ($this->EOF) return (defined('PEAR_ERROR_RETURN')) ? new PEAR_Error('EOF',-1): false;
2969		$arr = $this->fields;
2970		$this->MoveNext();
2971		return 1; // DB_OK
2972	}
2973
2974
2975	/**
2976	 * Move to the first row in the recordset. Many databases do NOT support this.
2977	 *
2978	 * @return true or false
2979	 */
2980	function MoveFirst()
2981	{
2982		if ($this->_currentRow == 0) return true;
2983		return $this->Move(0);
2984	}
2985
2986
2987	/**
2988	 * Move to the last row in the recordset.
2989	 *
2990	 * @return true or false
2991	 */
2992	function MoveLast()
2993	{
2994		if ($this->_numOfRows >= 0) return $this->Move($this->_numOfRows-1);
2995		if ($this->EOF) return false;
2996		while (!$this->EOF) {
2997			$f = $this->fields;
2998			$this->MoveNext();
2999		}
3000		$this->fields = $f;
3001		$this->EOF = false;
3002		return true;
3003	}
3004
3005
3006	/**
3007	 * Move to next record in the recordset.
3008	 *
3009	 * @return true if there still rows available, or false if there are no more rows (EOF).
3010	 */
3011	function MoveNext()
3012	{
3013		if (!$this->EOF) {
3014			$this->_currentRow++;
3015			if ($this->_fetch()) return true;
3016		}
3017		$this->EOF = true;
3018		/* -- tested error handling when scrolling cursor -- seems useless.
3019		$conn = $this->connection;
3020		if ($conn && $conn->raiseErrorFn && ($errno = $conn->ErrorNo())) {
3021			$fn = $conn->raiseErrorFn;
3022			$fn($conn->databaseType,'MOVENEXT',$errno,$conn->ErrorMsg().' ('.$this->sql.')',$conn->host,$conn->database);
3023		}
3024		*/
3025		return false;
3026	}
3027
3028
3029	/**
3030	 * Random access to a specific row in the recordset. Some databases do not support
3031	 * access to previous rows in the databases (no scrolling backwards).
3032	 *
3033	 * @param rowNumber is the row to move to (0-based)
3034	 *
3035	 * @return true if there still rows available, or false if there are no more rows (EOF).
3036	 */
3037	function Move($rowNumber = 0)
3038	{
3039		$this->EOF = false;
3040		if ($rowNumber == $this->_currentRow) return true;
3041		if ($rowNumber >= $this->_numOfRows)
3042	   		if ($this->_numOfRows != -1) $rowNumber = $this->_numOfRows-2;
3043
3044		if ($this->canSeek) {
3045
3046			if ($this->_seek($rowNumber)) {
3047				$this->_currentRow = $rowNumber;
3048				if ($this->_fetch()) {
3049					return true;
3050				}
3051			} else {
3052				$this->EOF = true;
3053				return false;
3054			}
3055		} else {
3056			if ($rowNumber < $this->_currentRow) return false;
3057			global $ADODB_EXTENSION;
3058			if ($ADODB_EXTENSION) {
3059				while (!$this->EOF && $this->_currentRow < $rowNumber) {
3060					adodb_movenext($this);
3061				}
3062			} else {
3063
3064				while (! $this->EOF && $this->_currentRow < $rowNumber) {
3065					$this->_currentRow++;
3066
3067					if (!$this->_fetch()) $this->EOF = true;
3068				}
3069			}
3070			return !($this->EOF);
3071		}
3072
3073		$this->fields = false;
3074		$this->EOF = true;
3075		return false;
3076	}
3077
3078
3079	/**
3080	 * Get the value of a field in the current row by column name.
3081	 * Will not work if ADODB_FETCH_MODE is set to ADODB_FETCH_NUM.
3082	 *
3083	 * @param colname  is the field to access
3084	 *
3085	 * @return the value of $colname column
3086	 */
3087	function Fields($colname)
3088	{
3089		return $this->fields[$colname];
3090	}
3091
3092	function GetAssocKeys($upper=true)
3093	{
3094		$this->bind = array();
3095		for ($i=0; $i < $this->_numOfFields; $i++) {
3096			$o = $this->FetchField($i);
3097			if ($upper === 2) $this->bind[$o->name] = $i;
3098			else $this->bind[($upper) ? strtoupper($o->name) : strtolower($o->name)] = $i;
3099		}
3100	}
3101
3102  /**
3103   * Use associative array to get fields array for databases that do not support
3104   * associative arrays. Submitted by Paolo S. Asioli paolo.asioli#libero.it
3105   *
3106   * If you don't want uppercase cols, set $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC
3107   * before you execute your SQL statement, and access $rs->fields['col'] directly.
3108   *
3109   * $upper  0 = lowercase, 1 = uppercase, 2 = whatever is returned by FetchField
3110   */
3111	function &GetRowAssoc($upper=1)
3112	{
3113		$record = array();
3114	 //	if (!$this->fields) return $record;
3115
3116	   	if (!$this->bind) {
3117			$this->GetAssocKeys($upper);
3118		}
3119
3120		foreach($this->bind as $k => $v) {
3121			$record[$k] = $this->fields[$v];
3122		}
3123
3124		return $record;
3125	}
3126
3127
3128	/**
3129	 * Clean up recordset
3130	 *
3131	 * @return true or false
3132	 */
3133	function Close()
3134	{
3135		// free connection object - this seems to globally free the object
3136		// and not merely the reference, so don't do this...
3137		// $this->connection = false;
3138		if (!$this->_closed) {
3139			$this->_closed = true;
3140			return $this->_close();
3141		} else
3142			return true;
3143	}
3144
3145	/**
3146	 * synonyms RecordCount and RowCount
3147	 *
3148	 * @return the number of rows or -1 if this is not supported
3149	 */
3150	function RecordCount() {return $this->_numOfRows;}
3151
3152
3153	/*
3154	* If we are using PageExecute(), this will return the maximum possible rows
3155	* that can be returned when paging a recordset.
3156	*/
3157	function MaxRecordCount()
3158	{
3159		return ($this->_maxRecordCount) ? $this->_maxRecordCount : $this->RecordCount();
3160	}
3161
3162	/**
3163	 * synonyms RecordCount and RowCount
3164	 *
3165	 * @return the number of rows or -1 if this is not supported
3166	 */
3167	function RowCount() {return $this->_numOfRows;}
3168
3169
3170	 /**
3171	 * Portable RecordCount. Pablo Roca <pabloroca@mvps.org>
3172	 *
3173	 * @return  the number of records from a previous SELECT. All databases support this.
3174	 *
3175	 * But aware possible problems in multiuser environments. For better speed the table
3176	 * must be indexed by the condition. Heavy test this before deploying.
3177	 */
3178	function PO_RecordCount($table="", $condition="") {
3179
3180		$lnumrows = $this->_numOfRows;
3181		// the database doesn't support native recordcount, so we do a workaround
3182		if ($lnumrows == -1 && $this->connection) {
3183			IF ($table) {
3184				if ($condition) $condition = " WHERE " . $condition;
3185				$resultrows = &$this->connection->Execute("SELECT COUNT(*) FROM $table $condition");
3186				if ($resultrows) $lnumrows = reset($resultrows->fields);
3187			}
3188		}
3189		return $lnumrows;
3190	}
3191
3192
3193	/**
3194	 * @return the current row in the recordset. If at EOF, will return the last row. 0-based.
3195	 */
3196	function CurrentRow() {return $this->_currentRow;}
3197
3198	/**
3199	 * synonym for CurrentRow -- for ADO compat
3200	 *
3201	 * @return the current row in the recordset. If at EOF, will return the last row. 0-based.
3202	 */
3203	function AbsolutePosition() {return $this->_currentRow;}
3204
3205	/**
3206	 * @return the number of columns in the recordset. Some databases will set this to 0
3207	 * if no records are returned, others will return the number of columns in the query.
3208	 */
3209	function FieldCount() {return $this->_numOfFields;}
3210
3211
3212	/**
3213	 * Get the ADOFieldObject of a specific column.
3214	 *
3215	 * @param fieldoffset	is the column position to access(0-based).
3216	 *
3217	 * @return the ADOFieldObject for that column, or false.
3218	 */
3219	function &FetchField($fieldoffset)
3220	{
3221		// must be defined by child class
3222	}
3223
3224	/**
3225	 * Get the ADOFieldObjects of all columns in an array.
3226	 *
3227	 */
3228	function& FieldTypesArray()
3229	{
3230		$arr = array();
3231		for ($i=0, $max=$this->_numOfFields; $i < $max; $i++)
3232			$arr[] = $this->FetchField($i);
3233		return $arr;
3234	}
3235
3236	/**
3237	* Return the fields array of the current row as an object for convenience.
3238	* The default case is lowercase field names.
3239	*
3240	* @return the object with the properties set to the fields of the current row
3241	*/
3242	function &FetchObj()
3243	{
3244		$o =& $this->FetchObject(false);
3245		return $o;
3246	}
3247
3248	/**
3249	* Return the fields array of the current row as an object for convenience.
3250	* The default case is uppercase.
3251	*
3252	* @param $isupper to set the object property names to uppercase
3253	*
3254	* @return the object with the properties set to the fields of the current row
3255	*/
3256	function &FetchObject($isupper=true)
3257	{
3258		if (empty($this->_obj)) {
3259			$this->_obj = new ADOFetchObj();
3260			$this->_names = array();
3261			for ($i=0; $i <$this->_numOfFields; $i++) {
3262				$f = $this->FetchField($i);
3263				$this->_names[] = $f->name;
3264			}
3265		}
3266		$i = 0;
3267		if (PHP_VERSION >= 5) $o = clone($this->_obj);
3268		else $o = $this->_obj;
3269
3270		for ($i=0; $i <$this->_numOfFields; $i++) {
3271			$name = $this->_names[$i];
3272			if ($isupper) $n = strtoupper($name);
3273			else $n = $name;
3274
3275			$o->$n = $this->Fields($name);
3276		}
3277		return $o;
3278	}
3279
3280	/**
3281	* Return the fields array of the current row as an object for convenience.
3282	* The default is lower-case field names.
3283	*
3284	* @return the object with the properties set to the fields of the current row,
3285	* 	or false if EOF
3286	*
3287	* Fixed bug reported by tim@orotech.net
3288	*/
3289	function &FetchNextObj()
3290	{
3291		$o =& $this->FetchNextObject(false);
3292		return $o;
3293	}
3294
3295
3296	/**
3297	* Return the fields array of the current row as an object for convenience.
3298	* The default is upper case field names.
3299	*
3300	* @param $isupper to set the object property names to uppercase
3301	*
3302	* @return the object with the properties set to the fields of the current row,
3303	* 	or false if EOF
3304	*
3305	* Fixed bug reported by tim@orotech.net
3306	*/
3307	function &FetchNextObject($isupper=true)
3308	{
3309		$o = false;
3310		if ($this->_numOfRows != 0 && !$this->EOF) {
3311			$o = $this->FetchObject($isupper);
3312			$this->_currentRow++;
3313			if ($this->_fetch()) return $o;
3314		}
3315		$this->EOF = true;
3316		return $o;
3317	}
3318
3319	/**
3320	 * Get the metatype of the column. This is used for formatting. This is because
3321	 * many databases use different names for the same type, so we transform the original
3322	 * type to our standardised version which uses 1 character codes:
3323	 *
3324	 * @param t  is the type passed in. Normally is ADOFieldObject->type.
3325	 * @param len is the maximum length of that field. This is because we treat character
3326	 * 	fields bigger than a certain size as a 'B' (blob).
3327	 * @param fieldobj is the field object returned by the database driver. Can hold
3328	 *	additional info (eg. primary_key for mysql).
3329	 *
3330	 * @return the general type of the data:
3331	 *	C for character < 250 chars
3332	 *	X for teXt (>= 250 chars)
3333	 *	B for Binary
3334	 * 	N for numeric or floating point
3335	 *	D for date
3336	 *	T for timestamp
3337	 * 	L for logical/Boolean
3338	 *	I for integer
3339	 *	R for autoincrement counter/integer
3340	 *
3341	 *
3342	*/
3343	function MetaType($t,$len=-1,$fieldobj=false)
3344	{
3345		if (is_object($t)) {
3346			$fieldobj = $t;
3347			$t = $fieldobj->type;
3348			$len = $fieldobj->max_length;
3349		}
3350	// changed in 2.32 to hashing instead of switch stmt for speed...
3351	static $typeMap = array(
3352		'VARCHAR' => 'C',
3353		'VARCHAR2' => 'C',
3354		'CHAR' => 'C',
3355		'C' => 'C',
3356		'STRING' => 'C',
3357		'NCHAR' => 'C',
3358		'NVARCHAR' => 'C',
3359		'VARYING' => 'C',
3360		'BPCHAR' => 'C',
3361		'CHARACTER' => 'C',
3362		'INTERVAL' => 'C',  # Postgres
3363		##
3364		'LONGCHAR' => 'X',
3365		'TEXT' => 'X',
3366		'NTEXT' => 'X',
3367		'M' => 'X',
3368		'X' => 'X',
3369		'CLOB' => 'X',
3370		'NCLOB' => 'X',
3371		'LVARCHAR' => 'X',
3372		##
3373		'BLOB' => 'B',
3374		'IMAGE' => 'B',
3375		'BINARY' => 'B',
3376		'VARBINARY' => 'B',
3377		'LONGBINARY' => 'B',
3378		'B' => 'B',
3379		##
3380		'YEAR' => 'D', // mysql
3381		'DATE' => 'D',
3382		'D' => 'D',
3383		##
3384		'TIME' => 'T',
3385		'TIMESTAMP' => 'T',
3386		'DATETIME' => 'T',
3387		'TIMESTAMPTZ' => 'T',
3388		'T' => 'T',
3389		##
3390		'BOOL' => 'L',
3391		'BOOLEAN' => 'L',
3392		'BIT' => 'L',
3393		'L' => 'L',
3394		##
3395		'COUNTER' => 'R',
3396		'R' => 'R',
3397		'SERIAL' => 'R', // ifx
3398		'INT IDENTITY' => 'R',
3399		##
3400		'INT' => 'I',
3401		'INT2' => 'I',
3402		'INT4' => 'I',
3403		'INT8' => 'I',
3404		'INTEGER' => 'I',
3405		'INTEGER UNSIGNED' => 'I',
3406		'SHORT' => 'I',
3407		'TINYINT' => 'I',
3408		'SMALLINT' => 'I',
3409		'I' => 'I',
3410		##
3411		'LONG' => 'N', // interbase is numeric, oci8 is blob
3412		'BIGINT' => 'N', // this is bigger than PHP 32-bit integers
3413		'DECIMAL' => 'N',
3414		'DEC' => 'N',
3415		'REAL' => 'N',
3416		'DOUBLE' => 'N',
3417		'DOUBLE PRECISION' => 'N',
3418		'SMALLFLOAT' => 'N',
3419		'FLOAT' => 'N',
3420		'NUMBER' => 'N',
3421		'NUM' => 'N',
3422		'NUMERIC' => 'N',
3423		'MONEY' => 'N',
3424
3425		## informix 9.2
3426		'SQLINT' => 'I',
3427		'SQLSERIAL' => 'I',
3428		'SQLSMINT' => 'I',
3429		'SQLSMFLOAT' => 'N',
3430		'SQLFLOAT' => 'N',
3431		'SQLMONEY' => 'N',
3432		'SQLDECIMAL' => 'N',
3433		'SQLDATE' => 'D',
3434		'SQLVCHAR' => 'C',
3435		'SQLCHAR' => 'C',
3436		'SQLDTIME' => 'T',
3437		'SQLINTERVAL' => 'N',
3438		'SQLBYTES' => 'B',
3439		'SQLTEXT' => 'X'
3440		);
3441
3442		$tmap = false;
3443		$t = strtoupper($t);
3444		$tmap = (isset($typeMap[$t])) ? $typeMap[$t] : 'N';
3445		switch ($tmap) {
3446		case 'C':
3447
3448			// is the char field is too long, return as text field...
3449			if ($this->blobSize >= 0) {
3450				if ($len > $this->blobSize) return 'X';
3451			} else if ($len > 250) {
3452				return 'X';
3453			}
3454			return 'C';
3455
3456		case 'I':
3457			if (!empty($fieldobj->primary_key)) return 'R';
3458			return 'I';
3459
3460		case false:
3461			return 'N';
3462
3463		case 'B':
3464			 if (isset($fieldobj->binary))
3465				 return ($fieldobj->binary) ? 'B' : 'X';
3466			return 'B';
3467
3468		case 'D':
3469			if (!empty($this->connection) && !empty($this->connection->datetime)) return 'T';
3470			return 'D';
3471
3472		default:
3473			if ($t == 'LONG' && $this->dataProvider == 'oci8') return 'B';
3474			return $tmap;
3475		}
3476	}
3477
3478	function _close() {}
3479
3480	/**
3481	 * set/returns the current recordset page when paginating
3482	 */
3483	function AbsolutePage($page=-1)
3484	{
3485		if ($page != -1) $this->_currentPage = $page;
3486		return $this->_currentPage;
3487	}
3488
3489	/**
3490	 * set/returns the status of the atFirstPage flag when paginating
3491	 */
3492	function AtFirstPage($status=false)
3493	{
3494		if ($status != false) $this->_atFirstPage = $status;
3495		return $this->_atFirstPage;
3496	}
3497
3498	function LastPageNo($page = false)
3499	{
3500		if ($page != false) $this->_lastPageNo = $page;
3501		return $this->_lastPageNo;
3502	}
3503
3504	/**
3505	 * set/returns the status of the atLastPage flag when paginating
3506	 */
3507	function AtLastPage($status=false)
3508	{
3509		if ($status != false) $this->_atLastPage = $status;
3510		return $this->_atLastPage;
3511	}
3512
3513} // end class ADORecordSet
3514
3515	//==============================================================================================
3516	// CLASS ADORecordSet_array
3517	//==============================================================================================
3518
3519	/**
3520	 * This class encapsulates the concept of a recordset created in memory
3521	 * as an array. This is useful for the creation of cached recordsets.
3522	 *
3523	 * Note that the constructor is different from the standard ADORecordSet
3524	 */
3525
3526	class ADORecordSet_array extends ADORecordSet
3527	{
3528		var $databaseType = 'array';
3529
3530		var $_array; 	// holds the 2-dimensional data array
3531		var $_types;	// the array of types of each column (C B I L M)
3532		var $_colnames;	// names of each column in array
3533		var $_skiprow1;	// skip 1st row because it holds column names
3534		var $_fieldarr; // holds array of field objects
3535		var $canSeek = true;
3536		var $affectedrows = false;
3537		var $insertid = false;
3538		var $sql = '';
3539		var $compat = false;
3540		/**
3541		 * Constructor
3542		 *
3543		 */
3544		function ADORecordSet_array($fakeid=1)
3545		{
3546		global $ADODB_FETCH_MODE,$ADODB_COMPAT_FETCH;
3547
3548			// fetch() on EOF does not delete $this->fields
3549			$this->compat = !empty($ADODB_COMPAT_FETCH);
3550			$this->ADORecordSet($fakeid); // fake queryID
3551			$this->fetchMode = $ADODB_FETCH_MODE;
3552		}
3553
3554
3555		/**
3556		 * Setup the array.
3557		 *
3558		 * @param array		is a 2-dimensional array holding the data.
3559		 *			The first row should hold the column names
3560		 *			unless paramter $colnames is used.
3561		 * @param typearr	holds an array of types. These are the same types
3562		 *			used in MetaTypes (C,B,L,I,N).
3563		 * @param [colnames]	array of column names. If set, then the first row of
3564		 *			$array should not hold the column names.
3565		 */
3566		function InitArray($array,$typearr,$colnames=false)
3567		{
3568			$this->_array = $array;
3569			$this->_types = $typearr;
3570			if ($colnames) {
3571				$this->_skiprow1 = false;
3572				$this->_colnames = $colnames;
3573			} else  {
3574				$this->_skiprow1 = true;
3575				$this->_colnames = $array[0];
3576			}
3577			$this->Init();
3578		}
3579		/**
3580		 * Setup the Array and datatype file objects
3581		 *
3582		 * @param array		is a 2-dimensional array holding the data.
3583		 *			The first row should hold the column names
3584		 *			unless paramter $colnames is used.
3585		 * @param fieldarr	holds an array of ADOFieldObject's.
3586		 */
3587		function InitArrayFields(&$array,&$fieldarr)
3588		{
3589			$this->_array =& $array;
3590			$this->_skiprow1= false;
3591			if ($fieldarr) {
3592				$this->_fieldobjects =& $fieldarr;
3593			}
3594			$this->Init();
3595		}
3596
3597		function &GetArray($nRows=-1)
3598		{
3599			if ($nRows == -1 && $this->_currentRow <= 0 && !$this->_skiprow1) {
3600				return $this->_array;
3601			} else {
3602				$arr =& ADORecordSet::GetArray($nRows);
3603				return $arr;
3604			}
3605		}
3606
3607		function _initrs()
3608		{
3609			$this->_numOfRows =  sizeof($this->_array);
3610			if ($this->_skiprow1) $this->_numOfRows -= 1;
3611
3612			$this->_numOfFields =(isset($this->_fieldobjects)) ?
3613				 sizeof($this->_fieldobjects):sizeof($this->_types);
3614		}
3615
3616		/* Use associative array to get fields array */
3617		function Fields($colname)
3618		{
3619			$mode = isset($this->adodbFetchMode) ? $this->adodbFetchMode : $this->fetchMode;
3620
3621			if ($mode & ADODB_FETCH_ASSOC) {
3622				if (!isset($this->fields[$colname])) $colname = strtolower($colname);
3623				return $this->fields[$colname];
3624			}
3625			if (!$this->bind) {
3626				$this->bind = array();
3627				for ($i=0; $i < $this->_numOfFields; $i++) {
3628					$o = $this->FetchField($i);
3629					$this->bind[strtoupper($o->name)] = $i;
3630				}
3631			}
3632			return $this->fields[$this->bind[strtoupper($colname)]];
3633		}
3634
3635		function &FetchField($fieldOffset = -1)
3636		{
3637			if (isset($this->_fieldobjects)) {
3638				return $this->_fieldobjects[$fieldOffset];
3639			}
3640			$o =  new ADOFieldObject();
3641			$o->name = $this->_colnames[$fieldOffset];
3642			$o->type =  $this->_types[$fieldOffset];
3643			$o->max_length = -1; // length not known
3644
3645			return $o;
3646		}
3647
3648		function _seek($row)
3649		{
3650			if (sizeof($this->_array) && 0 <= $row && $row < $this->_numOfRows) {
3651				$this->_currentRow = $row;
3652				if ($this->_skiprow1) $row += 1;
3653				$this->fields = $this->_array[$row];
3654				return true;
3655			}
3656			return false;
3657		}
3658
3659		function MoveNext()
3660		{
3661			if (!$this->EOF) {
3662				$this->_currentRow++;
3663
3664				$pos = $this->_currentRow;
3665
3666				if ($this->_numOfRows <= $pos) {
3667					if (!$this->compat) $this->fields = false;
3668				} else {
3669					if ($this->_skiprow1) $pos += 1;
3670					$this->fields = $this->_array[$pos];
3671					return true;
3672				}
3673				$this->EOF = true;
3674			}
3675
3676			return false;
3677		}
3678
3679		function _fetch()
3680		{
3681			$pos = $this->_currentRow;
3682
3683			if ($this->_numOfRows <= $pos) {
3684				if (!$this->compat) $this->fields = false;
3685				return false;
3686			}
3687			if ($this->_skiprow1) $pos += 1;
3688			$this->fields = $this->_array[$pos];
3689			return true;
3690		}
3691
3692		function _close()
3693		{
3694			return true;
3695		}
3696
3697	} // ADORecordSet_array
3698
3699	//==============================================================================================
3700	// HELPER FUNCTIONS
3701	//==============================================================================================
3702
3703	/**
3704	 * Synonym for ADOLoadCode. Private function. Do not use.
3705	 *
3706	 * @deprecated
3707	 */
3708	function ADOLoadDB($dbType)
3709	{
3710		return ADOLoadCode($dbType);
3711	}
3712
3713	/**
3714	 * Load the code for a specific database driver. Private function. Do not use.
3715	 */
3716	function ADOLoadCode($dbType)
3717	{
3718	global $ADODB_LASTDB;
3719
3720		if (!$dbType) return false;
3721		$db = strtolower($dbType);
3722		switch ($db) {
3723			case 'ado':
3724				if (PHP_VERSION >= 5) $db = 'ado5';
3725				$class = 'ado';
3726				break;
3727			case 'ifx':
3728			case 'maxsql': $class = $db = 'mysqlt'; break;
3729			case 'postgres':
3730			case 'postgres8':
3731			case 'pgsql': $class = $db = 'postgres7'; break;
3732			default:
3733				$class = $db; break;
3734		}
3735
3736		$file = ADODB_DIR."/drivers/adodb-".$db.".inc.php";
3737		@include_once($file);
3738		$ADODB_LASTDB = $class;
3739		if (class_exists("ADODB_" . $class)) return $class;
3740
3741		//ADOConnection::outp(adodb_pr(get_declared_classes(),true));
3742		if (!file_exists($file)) ADOConnection::outp("Missing file: $file");
3743		else ADOConnection::outp("Syntax error in file: $file");
3744		return false;
3745	}
3746
3747	/**
3748	 * synonym for ADONewConnection for people like me who cannot remember the correct name
3749	 */
3750	function &NewADOConnection($db='')
3751	{
3752		$tmp =& ADONewConnection($db);
3753		return $tmp;
3754	}
3755
3756	/**
3757	 * Instantiate a new Connection class for a specific database driver.
3758	 *
3759	 * @param [db]  is the database Connection object to create. If undefined,
3760	 * 	use the last database driver that was loaded by ADOLoadCode().
3761	 *
3762	 * @return the freshly created instance of the Connection class.
3763	 */
3764	function &ADONewConnection($db='')
3765	{
3766	GLOBAL $ADODB_NEWCONNECTION, $ADODB_LASTDB;
3767
3768		if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2);
3769		$errorfn = (defined('ADODB_ERROR_HANDLER')) ? ADODB_ERROR_HANDLER : false;
3770		$false = false;
3771		if ($at = strpos($db,'://')) {
3772			$origdsn = $db;
3773			if (PHP_VERSION < 5) $dsna = @parse_url($db);
3774			else {
3775				$fakedsn = 'fake'.substr($db,$at);
3776				$dsna = @parse_url($fakedsn);
3777				$dsna['scheme'] = substr($db,0,$at);
3778
3779				if (strncmp($db,'pdo',3) == 0) {
3780					$sch = explode('_',$dsna['scheme']);
3781					if (sizeof($sch)>1) {
3782						$dsna['host'] = isset($dsna['host']) ? rawurldecode($dsna['host']) : '';
3783						$dsna['host'] = rawurlencode($sch[1].':host='.rawurldecode($dsna['host']));
3784						$dsna['scheme'] = 'pdo';
3785					}
3786				}
3787			}
3788
3789			if (!$dsna) {
3790				// special handling of oracle, which might not have host
3791				$db = str_replace('@/','@adodb-fakehost/',$db);
3792				$dsna = parse_url($db);
3793				if (!$dsna) return $false;
3794				$dsna['host'] = '';
3795			}
3796			$db = @$dsna['scheme'];
3797			if (!$db) return $false;
3798			$dsna['host'] = isset($dsna['host']) ? rawurldecode($dsna['host']) : '';
3799			$dsna['user'] = isset($dsna['user']) ? rawurldecode($dsna['user']) : '';
3800			$dsna['pass'] = isset($dsna['pass']) ? rawurldecode($dsna['pass']) : '';
3801			$dsna['path'] = isset($dsna['path']) ? rawurldecode(substr($dsna['path'],1)) : ''; # strip off initial /
3802
3803			if (isset($dsna['query'])) {
3804				$opt1 = explode('&',$dsna['query']);
3805				foreach($opt1 as $k => $v) {
3806					$arr = explode('=',$v);
3807					$opt[$arr[0]] = isset($arr[1]) ? rawurldecode($arr[1]) : 1;
3808				}
3809			} else $opt = array();
3810		}
3811	/*
3812	 *  phptype: Database backend used in PHP (mysql, odbc etc.)
3813	 *  dbsyntax: Database used with regards to SQL syntax etc.
3814	 *  protocol: Communication protocol to use (tcp, unix etc.)
3815	 *  hostspec: Host specification (hostname[:port])
3816	 *  database: Database to use on the DBMS server
3817	 *  username: User name for login
3818	 *  password: Password for login
3819	 */
3820		if (!empty($ADODB_NEWCONNECTION)) {
3821			$obj = $ADODB_NEWCONNECTION($db);
3822
3823		} else {
3824
3825			if (!isset($ADODB_LASTDB)) $ADODB_LASTDB = '';
3826			if (empty($db)) $db = $ADODB_LASTDB;
3827
3828			if ($db != $ADODB_LASTDB) $db = ADOLoadCode($db);
3829
3830			if (!$db) {
3831				if (isset($origdsn)) $db = $origdsn;
3832				if ($errorfn) {
3833					// raise an error
3834					$ignore = false;
3835					$errorfn('ADONewConnection', 'ADONewConnection', -998,
3836							 "could not load the database driver for '$db'",
3837							 $db,false,$ignore);
3838				} else
3839					 ADOConnection::outp( "<p>ADONewConnection: Unable to load database driver '$db'</p>",false);
3840
3841				return $false;
3842			}
3843
3844			$cls = 'ADODB_'.$db;
3845			if (!class_exists($cls)) {
3846				adodb_backtrace();
3847				return $false;
3848			}
3849
3850			$obj = new $cls();
3851		}
3852
3853		# constructor should not fail
3854		if ($obj) {
3855			if ($errorfn)  $obj->raiseErrorFn = $errorfn;
3856			if (isset($dsna)) {
3857				if (isset($dsna['port'])) $obj->port = $dsna['port'];
3858				foreach($opt as $k => $v) {
3859					switch(strtolower($k)) {
3860					case 'new':
3861										$nconnect = true; $persist = true; break;
3862					case 'persist':
3863					case 'persistent': 	$persist = $v; break;
3864					case 'debug':		$obj->debug = (integer) $v; break;
3865					#ibase
3866					case 'role':		$obj->role = $v; break;
3867					case 'dialect': 	$obj->dialect = (integer) $v; break;
3868					case 'charset':		$obj->charset = $v; $obj->charSet=$v; break;
3869					case 'buffers':		$obj->buffers = $v; break;
3870					case 'fetchmode':   $obj->SetFetchMode($v); break;
3871					#ado
3872					case 'charpage':	$obj->charPage = $v; break;
3873					#mysql, mysqli
3874					case 'clientflags': $obj->clientFlags = $v; break;
3875					#mysql, mysqli, postgres
3876					case 'port': $obj->port = $v; break;
3877					#mysqli
3878					case 'socket': $obj->socket = $v; break;
3879					#oci8
3880					case 'nls_date_format': $obj->NLS_DATE_FORMAT = $v; break;
3881					}
3882				}
3883				if (empty($persist))
3884					$ok = $obj->Connect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
3885				else if (empty($nconnect))
3886					$ok = $obj->PConnect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
3887				else
3888					$ok = $obj->NConnect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
3889
3890				if (!$ok) return $false;
3891			}
3892		}
3893		return $obj;
3894	}
3895
3896
3897
3898	// $perf == true means called by NewPerfMonitor(), otherwise for data dictionary
3899	function _adodb_getdriver($provider,$drivername,$perf=false)
3900	{
3901		switch ($provider) {
3902		case 'odbtp':   if (strncmp('odbtp_',$drivername,6)==0) return substr($drivername,6);
3903		case 'odbc' :   if (strncmp('odbc_',$drivername,5)==0) return substr($drivername,5);
3904		case 'ado'  :   if (strncmp('ado_',$drivername,4)==0) return substr($drivername,4);
3905		case 'native':  break;
3906		default:
3907			return $provider;
3908		}
3909
3910		switch($drivername) {
3911		case 'mysqlt':
3912		case 'mysqli':
3913				$drivername='mysql';
3914				break;
3915		case 'postgres7':
3916		case 'postgres8':
3917				$drivername = 'postgres';
3918				break;
3919		case 'firebird15': $drivername = 'firebird'; break;
3920		case 'oracle': $drivername = 'oci8'; break;
3921		case 'access': if ($perf) $drivername = ''; break;
3922		case 'db2'   : break;
3923		case 'sapdb' : break;
3924		default:
3925			$drivername = 'generic';
3926			break;
3927		}
3928		return $drivername;
3929	}
3930
3931	function &NewPerfMonitor(&$conn)
3932	{
3933		$false = false;
3934		$drivername = _adodb_getdriver($conn->dataProvider,$conn->databaseType,true);
3935		if (!$drivername || $drivername == 'generic') return $false;
3936		include_once(ADODB_DIR.'/adodb-perf.inc.php');
3937		@include_once(ADODB_DIR."/perf/perf-$drivername.inc.php");
3938		$class = "Perf_$drivername";
3939		if (!class_exists($class)) return $false;
3940		$perf = new $class($conn);
3941
3942		return $perf;
3943	}
3944
3945	function &NewDataDictionary(&$conn,$drivername=false)
3946	{
3947		$false = false;
3948		if (!$drivername) $drivername = _adodb_getdriver($conn->dataProvider,$conn->databaseType);
3949
3950		include_once(ADODB_DIR.'/adodb-lib.inc.php');
3951		include_once(ADODB_DIR.'/adodb-datadict.inc.php');
3952		$path = ADODB_DIR."/datadict/datadict-$drivername.inc.php";
3953
3954		if (!file_exists($path)) {
3955			ADOConnection::outp("Database driver '$path' not available");
3956			return $false;
3957		}
3958		include_once($path);
3959		$class = "ADODB2_$drivername";
3960		$dict = new $class();
3961		$dict->dataProvider = $conn->dataProvider;
3962		$dict->connection = &$conn;
3963		$dict->upperName = strtoupper($drivername);
3964		$dict->quote = $conn->nameQuote;
3965		if (!empty($conn->_connectionID))
3966			$dict->serverInfo = $conn->ServerInfo();
3967
3968		return $dict;
3969	}
3970
3971
3972
3973	/*
3974		Perform a print_r, with pre tags for better formatting.
3975	*/
3976	function adodb_pr($var,$as_string=false)
3977	{
3978		if ($as_string) ob_start();
3979
3980		if (isset($_SERVER['HTTP_USER_AGENT'])) {
3981			echo " <pre>\n";print_r($var);echo "</pre>\n";
3982		} else
3983			print_r($var);
3984
3985		if ($as_string) {
3986			$s = ob_get_contents();
3987			ob_end_clean();
3988			return $s;
3989		}
3990	}
3991
3992	/*
3993		Perform a stack-crawl and pretty print it.
3994
3995		@param printOrArr  Pass in a boolean to indicate print, or an $exception->trace array (assumes that print is true then).
3996		@param levels Number of levels to display
3997	*/
3998	function adodb_backtrace($printOrArr=true,$levels=9999)
3999	{
4000		global $ADODB_INCLUDED_LIB;
4001		if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php');
4002		return _adodb_backtrace($printOrArr,$levels);
4003	}
4004
4005
4006}
4007?>
4008