1<?php
2/*
3 * Set tabs to 4 for best viewing.
4 *
5 * Latest version is available at http://adodb.org/
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   v5.20.16  12-Jan-2020
18	@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
19	@copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
20
21	Released under both BSD license and Lesser GPL library license. You can choose which license
22	you prefer.
23
24	PHP's database access functions are not standardised. This creates a need for a database
25	class library to hide the differences between the different database API's (encapsulate
26	the differences) so we can easily switch databases.
27
28	We currently support MySQL, Oracle, Microsoft SQL Server, Sybase, Sybase SQL Anywhere, DB2,
29	Informix, PostgreSQL, FrontBase, Interbase (Firebird and Borland variants), Foxpro, Access,
30	ADO, SAP DB, SQLite and ODBC. We have had successful reports of connecting to Progress and
31	other databases via ODBC.
32 */
33
34if (!defined('_ADODB_LAYER')) {
35	define('_ADODB_LAYER',1);
36
37	// The ADOdb extension is no longer maintained and effectively unsupported
38	// since v5.04. The library will not function properly if it is present.
39	if(defined('ADODB_EXTENSION')) {
40		$msg = "Unsupported ADOdb Extension (v" . ADODB_EXTENSION . ") detected! "
41			. "Disable it to use ADOdb";
42
43		$errorfn = defined('ADODB_ERROR_HANDLER') ? ADODB_ERROR_HANDLER : false;
44		if ($errorfn) {
45			$conn = false;
46			$errorfn('ADOdb', basename(__FILE__), -9999, $msg, null, null, $conn);
47		} else {
48			die($msg . PHP_EOL);
49		}
50	}
51
52	//==============================================================================================
53	// CONSTANT DEFINITIONS
54	//==============================================================================================
55
56
57	/**
58	 * Set ADODB_DIR to the directory where this file resides...
59	 * This constant was formerly called $ADODB_RootPath
60	 */
61	if (!defined('ADODB_DIR')) {
62		define('ADODB_DIR',dirname(__FILE__));
63	}
64
65	//==============================================================================================
66	// GLOBAL VARIABLES
67	//==============================================================================================
68
69	GLOBAL
70		$ADODB_vers,		// database version
71		$ADODB_COUNTRECS,	// count number of records returned - slows down query
72		$ADODB_CACHE_DIR,	// directory to cache recordsets
73		$ADODB_CACHE,
74		$ADODB_CACHE_CLASS,
75		$ADODB_EXTENSION,   // ADODB extension installed
76		$ADODB_COMPAT_FETCH, // If $ADODB_COUNTRECS and this is true, $rs->fields is available on EOF
77		$ADODB_FETCH_MODE,	// DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default...
78		$ADODB_GETONE_EOF,
79		$ADODB_QUOTE_FIELDNAMES; // Allows you to force quotes (backticks) around field names in queries generated by getinsertsql and getupdatesql.
80
81	//==============================================================================================
82	// GLOBAL SETUP
83	//==============================================================================================
84
85	$ADODB_EXTENSION = defined('ADODB_EXTENSION');
86
87	// ********************************************************
88	// Controls $ADODB_FORCE_TYPE mode. Default is ADODB_FORCE_VALUE (3).
89	// Used in GetUpdateSql and GetInsertSql functions. Thx to Niko, nuko#mbnet.fi
90	//
91	// 0 = ignore empty fields. All empty fields in array are ignored.
92	// 1 = force null. All empty, php null and string 'null' fields are changed to sql NULL values.
93	// 2 = force empty. All empty, php null and string 'null' fields are changed to sql empty '' or 0 values.
94	// 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.
95
96		define('ADODB_FORCE_IGNORE',0);
97		define('ADODB_FORCE_NULL',1);
98		define('ADODB_FORCE_EMPTY',2);
99		define('ADODB_FORCE_VALUE',3);
100	// ********************************************************
101
102
103	if (!$ADODB_EXTENSION || ADODB_EXTENSION < 4.0) {
104
105		define('ADODB_BAD_RS','<p>Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;</p>');
106
107	// allow [ ] @ ` " and . in table names
108		define('ADODB_TABLE_REGEX','([]0-9a-z_\:\"\`\.\@\[-]*)');
109
110	// prefetching used by oracle
111		if (!defined('ADODB_PREFETCH_ROWS')) {
112			define('ADODB_PREFETCH_ROWS',10);
113		}
114
115
116	/**
117	 * Fetch mode
118	 *
119	 * Set global variable $ADODB_FETCH_MODE to one of these constants or use
120	 * the SetFetchMode() method to control how recordset fields are returned
121	 * when fetching data.
122	 *
123	 *   - NUM:     array()
124	 *   - ASSOC:   array('id' => 456, 'name' => 'john')
125	 *   - BOTH:    array(0 => 456, 'id' => 456, 1 => 'john', 'name' => 'john')
126	 *   - DEFAULT: driver-dependent
127	 */
128		define('ADODB_FETCH_DEFAULT', 0);
129		define('ADODB_FETCH_NUM', 1);
130		define('ADODB_FETCH_ASSOC', 2);
131		define('ADODB_FETCH_BOTH', 3);
132
133	/**
134	 * Associative array case constants
135	 *
136	 * By defining the ADODB_ASSOC_CASE constant to one of these values, it is
137	 * possible to control the case of field names (associative array's keys)
138	 * when operating in ADODB_FETCH_ASSOC fetch mode.
139	 *   - LOWER:  $rs->fields['orderid']
140	 *   - UPPER:  $rs->fields['ORDERID']
141	 *   - NATIVE: $rs->fields['OrderID'] (or whatever the RDBMS will return)
142	 *
143	 * The default is to use native case-names.
144	 *
145	 * NOTE: This functionality is not implemented everywhere, it currently
146	 * works only with: mssql, odbc, oci8 and ibase derived drivers
147	 */
148		define('ADODB_ASSOC_CASE_LOWER', 0);
149		define('ADODB_ASSOC_CASE_UPPER', 1);
150		define('ADODB_ASSOC_CASE_NATIVE', 2);
151
152
153		if (!defined('TIMESTAMP_FIRST_YEAR')) {
154			define('TIMESTAMP_FIRST_YEAR',100);
155		}
156
157		/**
158		 * AutoExecute constants
159		 * (moved from adodb-pear.inc.php since they are only used in here)
160		 */
161		define('DB_AUTOQUERY_INSERT', 1);
162		define('DB_AUTOQUERY_UPDATE', 2);
163
164
165		// PHP's version scheme makes converting to numbers difficult - workaround
166		$_adodb_ver = (float) PHP_VERSION;
167		if ($_adodb_ver >= 5.2) {
168			define('ADODB_PHPVER',0x5200);
169		} else if ($_adodb_ver >= 5.0) {
170			define('ADODB_PHPVER',0x5000);
171		} else {
172			die("PHP5 or later required. You are running ".PHP_VERSION);
173		}
174		unset($_adodb_ver);
175	}
176
177
178	/**
179		Accepts $src and $dest arrays, replacing string $data
180	*/
181	function ADODB_str_replace($src, $dest, $data) {
182		if (ADODB_PHPVER >= 0x4050) {
183			return str_replace($src,$dest,$data);
184		}
185
186		$s = reset($src);
187		$d = reset($dest);
188		while ($s !== false) {
189			$data = str_replace($s,$d,$data);
190			$s = next($src);
191			$d = next($dest);
192		}
193		return $data;
194	}
195
196	function ADODB_Setup() {
197	GLOBAL
198		$ADODB_vers,		// database version
199		$ADODB_COUNTRECS,	// count number of records returned - slows down query
200		$ADODB_CACHE_DIR,	// directory to cache recordsets
201		$ADODB_FETCH_MODE,
202		$ADODB_CACHE,
203		$ADODB_CACHE_CLASS,
204		$ADODB_FORCE_TYPE,
205		$ADODB_GETONE_EOF,
206		$ADODB_QUOTE_FIELDNAMES;
207
208		if (empty($ADODB_CACHE_CLASS)) {
209			$ADODB_CACHE_CLASS =  'ADODB_Cache_File' ;
210		}
211		$ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT;
212		$ADODB_FORCE_TYPE = ADODB_FORCE_VALUE;
213		$ADODB_GETONE_EOF = null;
214
215		if (!isset($ADODB_CACHE_DIR)) {
216			$ADODB_CACHE_DIR = '/tmp'; //(isset($_ENV['TMP'])) ? $_ENV['TMP'] : '/tmp';
217		} else {
218			// do not accept url based paths, eg. http:/ or ftp:/
219			if (strpos($ADODB_CACHE_DIR,'://') !== false) {
220				die("Illegal path http:// or ftp://");
221			}
222		}
223
224		/**
225		 * ADODB version as a string.
226		 */
227		$ADODB_vers = 'v5.20.16  12-Jan-2020';
228
229		/**
230		 * Determines whether recordset->RecordCount() is used.
231		 * Set to false for highest performance -- RecordCount() will always return -1 then
232		 * for databases that provide "virtual" recordcounts...
233		 */
234		if (!isset($ADODB_COUNTRECS)) {
235			$ADODB_COUNTRECS = true;
236		}
237	}
238
239
240	//==============================================================================================
241	// CHANGE NOTHING BELOW UNLESS YOU ARE DESIGNING ADODB
242	//==============================================================================================
243
244	ADODB_Setup();
245
246	//==============================================================================================
247	// CLASS ADOFieldObject
248	//==============================================================================================
249	/**
250	 * Helper class for FetchFields -- holds info on a column
251	 */
252	class ADOFieldObject {
253		var $name = '';
254		var $max_length=0;
255		var $type="";
256/*
257		// additional fields by dannym... (danny_milo@yahoo.com)
258		var $not_null = false;
259		// actually, this has already been built-in in the postgres, fbsql AND mysql module? ^-^
260		// so we can as well make not_null standard (leaving it at "false" does not harm anyways)
261
262		var $has_default = false; // this one I have done only in mysql and postgres for now ...
263			// others to come (dannym)
264		var $default_value; // default, if any, and supported. Check has_default first.
265*/
266	}
267
268
269	function _adodb_safedate($s) {
270		return str_replace(array("'", '\\'), '', $s);
271	}
272
273	// parse date string to prevent injection attack
274	// date string will have one quote at beginning e.g. '3434343'
275	function _adodb_safedateq($s) {
276		$len = strlen($s);
277		if ($s[0] !== "'") {
278			$s2 = "'".$s[0];
279		} else {
280			$s2 = "'";
281		}
282		for($i=1; $i<$len; $i++) {
283			$ch = $s[$i];
284			if ($ch === '\\') {
285				$s2 .= "'";
286				break;
287			} elseif ($ch === "'") {
288				$s2 .= $ch;
289				break;
290			}
291
292			$s2 .= $ch;
293		}
294
295		return strlen($s2) == 0 ? 'null' : $s2;
296	}
297
298
299	// for transaction handling
300
301	function ADODB_TransMonitor($dbms, $fn, $errno, $errmsg, $p1, $p2, &$thisConnection) {
302		//print "Errorno ($fn errno=$errno m=$errmsg) ";
303		$thisConnection->_transOK = false;
304		if ($thisConnection->_oldRaiseFn) {
305			$fn = $thisConnection->_oldRaiseFn;
306			$fn($dbms, $fn, $errno, $errmsg, $p1, $p2,$thisConnection);
307		}
308	}
309
310	//------------------
311	// class for caching
312	class ADODB_Cache_File {
313
314		var $createdir = true; // requires creation of temp dirs
315
316		function __construct() {
317			global $ADODB_INCLUDED_CSV;
318			if (empty($ADODB_INCLUDED_CSV)) {
319				include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
320			}
321		}
322
323		// write serialised recordset to cache item/file
324		function writecache($filename, $contents,  $debug, $secs2cache) {
325			return adodb_write_file($filename, $contents,$debug);
326		}
327
328		// load serialised recordset and unserialise it
329		function &readcache($filename, &$err, $secs2cache, $rsClass) {
330			$rs = csv2rs($filename,$err,$secs2cache,$rsClass);
331			return $rs;
332		}
333
334		// flush all items in cache
335		function flushall($debug=false) {
336			global $ADODB_CACHE_DIR;
337
338			$rez = false;
339
340			if (strlen($ADODB_CACHE_DIR) > 1) {
341				$rez = $this->_dirFlush($ADODB_CACHE_DIR);
342				if ($debug) {
343					ADOConnection::outp( "flushall: $ADODB_CACHE_DIR<br><pre>\n". $rez."</pre>");
344				}
345			}
346			return $rez;
347		}
348
349		// flush one file in cache
350		function flushcache($f, $debug=false) {
351			if (!@unlink($f)) {
352				if ($debug) {
353					ADOConnection::outp( "flushcache: failed for $f");
354				}
355			}
356		}
357
358		function getdirname($hash) {
359			global $ADODB_CACHE_DIR;
360			if (!isset($this->notSafeMode)) {
361				$this->notSafeMode = !ini_get('safe_mode');
362			}
363			return ($this->notSafeMode) ? $ADODB_CACHE_DIR.'/'.substr($hash,0,2) : $ADODB_CACHE_DIR;
364		}
365
366		// create temp directories
367		function createdir($hash, $debug) {
368			global $ADODB_CACHE_PERMS;
369
370			$dir = $this->getdirname($hash);
371			if ($this->notSafeMode && !file_exists($dir)) {
372				$oldu = umask(0);
373				if (!@mkdir($dir, empty($ADODB_CACHE_PERMS) ? 0771 : $ADODB_CACHE_PERMS)) {
374					if(!is_dir($dir) && $debug) {
375						ADOConnection::outp("Cannot create $dir");
376					}
377				}
378				umask($oldu);
379			}
380
381			return $dir;
382		}
383
384		/**
385		* Private function to erase all of the files and subdirectories in a directory.
386		*
387		* Just specify the directory, and tell it if you want to delete the directory or just clear it out.
388		* Note: $kill_top_level is used internally in the function to flush subdirectories.
389		*/
390		function _dirFlush($dir, $kill_top_level = false) {
391			if(!$dh = @opendir($dir)) return;
392
393			while (($obj = readdir($dh))) {
394				if($obj=='.' || $obj=='..') continue;
395				$f = $dir.'/'.$obj;
396
397				if (strpos($obj,'.cache')) {
398					@unlink($f);
399				}
400				if (is_dir($f)) {
401					$this->_dirFlush($f, true);
402				}
403			}
404			if ($kill_top_level === true) {
405				@rmdir($dir);
406			}
407			return true;
408		}
409	}
410
411	//==============================================================================================
412	// CLASS ADOConnection
413	//==============================================================================================
414
415	/**
416	 * Connection object. For connecting to databases, and executing queries.
417	 */
418	abstract class ADOConnection {
419	//
420	// PUBLIC VARS
421	//
422	var $dataProvider = 'native';
423	var $databaseType = '';		/// RDBMS currently in use, eg. odbc, mysql, mssql
424	var $database = '';			/// Name of database to be used.
425	var $host = '';				/// The hostname of the database server
426	var $port = '';				/// The port of the database server
427	var $user = '';				/// The username which is used to connect to the database server.
428	var $password = '';			/// Password for the username. For security, we no longer store it.
429	var $debug = false;			/// if set to true will output sql statements
430	var $maxblobsize = 262144;	/// maximum size of blobs or large text fields (262144 = 256K)-- some db's die otherwise like foxpro
431	var $concat_operator = '+'; /// default concat operator -- change to || for Oracle/Interbase
432	var $substr = 'substr';		/// substring operator
433	var $length = 'length';		/// string length ofperator
434	var $random = 'rand()';		/// random function
435	var $upperCase = 'upper';		/// uppercase function
436	var $fmtDate = "'Y-m-d'";	/// used by DBDate() as the default date format used by the database
437	var $fmtTimeStamp = "'Y-m-d, h:i:s A'"; /// used by DBTimeStamp as the default timestamp fmt.
438	var $true = '1';			/// string that represents TRUE for a database
439	var $false = '0';			/// string that represents FALSE for a database
440	var $replaceQuote = "\\'";	/// string to use to replace quotes
441	var $nameQuote = '"';		/// string to use to quote identifiers and names
442	var $charSet=false;			/// character set to use - only for interbase, postgres and oci8
443	var $metaDatabasesSQL = '';
444	var $metaTablesSQL = '';
445	var $uniqueOrderBy = false; /// All order by columns have to be unique
446	var $emptyDate = '&nbsp;';
447	var $emptyTimeStamp = '&nbsp;';
448	var $lastInsID = false;
449	//--
450	var $hasInsertID = false;		/// supports autoincrement ID?
451	var $hasAffectedRows = false;	/// supports affected rows for update/delete?
452	var $hasTop = false;			/// support mssql/access SELECT TOP 10 * FROM TABLE
453	var $hasLimit = false;			/// support pgsql/mysql SELECT * FROM TABLE LIMIT 10
454	var $readOnly = false;			/// this is a readonly database - used by phpLens
455	var $hasMoveFirst = false;		/// has ability to run MoveFirst(), scrolling backwards
456	var $hasGenID = false;			/// can generate sequences using GenID();
457	var $hasTransactions = true;	/// has transactions
458	//--
459	var $genID = 0;					/// sequence id used by GenID();
460	var $raiseErrorFn = false;		/// error function to call
461	var $isoDates = false;			/// accepts dates in ISO format
462	var $cacheSecs = 3600;			/// cache for 1 hour
463
464	// memcache
465	var $memCache = false; /// should we use memCache instead of caching in files
466	var $memCacheHost; /// memCache host
467	var $memCachePort = 11211; /// memCache port
468	var $memCacheCompress = false; /// Use 'true' to store the item compressed (uses zlib)
469
470	var $sysDate = false; /// name of function that returns the current date
471	var $sysTimeStamp = false; /// name of function that returns the current timestamp
472	var $sysUTimeStamp = false; // name of function that returns the current timestamp accurate to the microsecond or nearest fraction
473	var $arrayClass = 'ADORecordSet_array'; /// name of class used to generate array recordsets, which are pre-downloaded recordsets
474
475	var $noNullStrings = false; /// oracle specific stuff - if true ensures that '' is converted to ' '
476	var $numCacheHits = 0;
477	var $numCacheMisses = 0;
478	var $pageExecuteCountRows = true;
479	var $uniqueSort = false; /// indicates that all fields in order by must be unique
480	var $leftOuter = false; /// operator to use for left outer join in WHERE clause
481	var $rightOuter = false; /// operator to use for right outer join in WHERE clause
482	var $ansiOuter = false; /// whether ansi outer join syntax supported
483	var $autoRollback = false; // autoRollback on PConnect().
484	var $poorAffectedRows = false; // affectedRows not working or unreliable
485
486	var $fnExecute = false;
487	var $fnCacheExecute = false;
488	var $blobEncodeType = false; // false=not required, 'I'=encode to integer, 'C'=encode to char
489	var $rsPrefix = "ADORecordSet_";
490
491	var $autoCommit = true;		/// do not modify this yourself - actually private
492	var $transOff = 0;			/// temporarily disable transactions
493	var $transCnt = 0;			/// count of nested transactions
494
495	var $fetchMode=false;
496
497	var $null2null = 'null'; // in autoexecute/getinsertsql/getupdatesql, this value will be converted to a null
498	var $bulkBind = false; // enable 2D Execute array
499	//
500	// PRIVATE VARS
501	//
502	var $_oldRaiseFn =  false;
503	var $_transOK = null;
504	var $_connectionID	= false;	/// The returned link identifier whenever a successful database connection is made.
505	var $_errorMsg = false;		/// A variable which was used to keep the returned last error message.  The value will
506								/// then returned by the errorMsg() function
507	var $_errorCode = false;	/// Last error code, not guaranteed to be used - only by oci8
508	var $_queryID = false;		/// This variable keeps the last created result link identifier
509
510	var $_isPersistentConnection = false;	/// A boolean variable to state whether its a persistent connection or normal connection.	*/
511	var $_bindInputArray = false; /// set to true if ADOConnection.Execute() permits binding of array parameters.
512	var $_evalAll = false;
513	var $_affected = false;
514	var $_logsql = false;
515	var $_transmode = ''; // transaction mode
516
517	/*
518	 * Additional parameters that may be passed to drivers in the connect string
519	 * Driver must be coded to accept the parameters
520	 */
521	protected $connectionParameters = array();
522
523	/**
524	* Adds a parameter to the connection string.
525	*
526	* These parameters are added to the connection string when connecting,
527	* if the driver is coded to use it.
528	*
529	* @param	string	$parameter	The name of the parameter to set
530	* @param	string	$value		The value of the parameter
531	*
532	* @return null
533	*
534	* @example, for mssqlnative driver ('CharacterSet','UTF-8')
535	*/
536	final public function setConnectionParameter($parameter,$value)
537	{
538
539		$this->connectionParameters[$parameter] = $value;
540
541	}
542
543	static function Version() {
544		global $ADODB_vers;
545
546		// Semantic Version number matching regex
547		$regex = '^[vV]?(\d+\.\d+\.\d+'         // Version number (X.Y.Z) with optional 'V'
548			. '(?:-(?:'                         // Optional preprod version: a '-'
549			. 'dev|'                            // followed by 'dev'
550			. '(?:(?:alpha|beta|rc)(?:\.\d+))'  // or a preprod suffix and version number
551			. '))?)(?:\s|$)';                   // Whitespace or end of string
552
553		if (!preg_match("/$regex/", $ADODB_vers, $matches)) {
554			// This should normally not happen... Return whatever is between the start
555			// of the string and the first whitespace (or the end of the string).
556			self::outp("Invalid version number: '$ADODB_vers'", 'Version');
557			$regex = '^[vV]?(.*?)(?:\s|$)';
558			preg_match("/$regex/", $ADODB_vers, $matches);
559		}
560		return $matches[1];
561	}
562
563	/**
564		Get server version info...
565
566		@returns An array with 2 elements: $arr['string'] is the description string,
567			and $arr[version] is the version (also a string).
568	*/
569	function ServerInfo() {
570		return array('description' => '', 'version' => '');
571	}
572
573	function IsConnected() {
574		return !empty($this->_connectionID);
575	}
576
577	function _findvers($str) {
578		if (preg_match('/([0-9]+\.([0-9\.])+)/',$str, $arr)) {
579			return $arr[1];
580		} else {
581			return '';
582		}
583	}
584
585	/**
586	* All error messages go through this bottleneck function.
587	* You can define your own handler by defining the function name in ADODB_OUTP.
588	*/
589	static function outp($msg,$newline=true) {
590		global $ADODB_FLUSH,$ADODB_OUTP;
591
592		if (defined('ADODB_OUTP')) {
593			$fn = ADODB_OUTP;
594			$fn($msg,$newline);
595			return;
596		} else if (isset($ADODB_OUTP)) {
597			$fn = $ADODB_OUTP;
598			$fn($msg,$newline);
599			return;
600		}
601
602		if ($newline) {
603			$msg .= "<br>\n";
604		}
605
606		if (isset($_SERVER['HTTP_USER_AGENT']) || !$newline) {
607			echo $msg;
608		} else {
609			echo strip_tags($msg);
610		}
611
612
613		if (!empty($ADODB_FLUSH) && ob_get_length() !== false) {
614			flush(); //  do not flush if output buffering enabled - useless - thx to Jesse Mullan
615		}
616
617	}
618
619	function Time() {
620		$rs = $this->_Execute("select $this->sysTimeStamp");
621		if ($rs && !$rs->EOF) {
622			return $this->UnixTimeStamp(reset($rs->fields));
623		}
624
625		return false;
626	}
627
628	/**
629	 * Parses the hostname to extract the port.
630	 * Overwrites $this->host and $this->port, only if a port is specified.
631	 * The Hostname can be fully or partially qualified,
632	 * ie: "db.mydomain.com:5432" or "ldaps://ldap.mydomain.com:636"
633	 * Any specified scheme such as ldap:// or ldaps:// is maintained.
634	 */
635	protected function parseHostNameAndPort() {
636		$parsed_url = parse_url($this->host);
637		if (is_array($parsed_url) && isset($parsed_url['host']) && isset($parsed_url['port'])) {
638			if ( isset($parsed_url['scheme']) ) {
639				// If scheme is specified (ie: ldap:// or ldaps://, make sure we retain that.
640				$this->host = $parsed_url['scheme'] . "://" . $parsed_url['host'];
641			} else {
642				$this->host = $parsed_url['host'];
643			}
644			$this->port = $parsed_url['port'];
645		}
646	}
647
648	/**
649	 * Connect to database
650	 *
651	 * @param [argHostname]		Host to connect to
652	 * @param [argUsername]		Userid to login
653	 * @param [argPassword]		Associated password
654	 * @param [argDatabaseName]	database
655	 * @param [forceNew]		force new connection
656	 *
657	 * @return true or false
658	 */
659	function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "", $forceNew = false) {
660		if ($argHostname != "") {
661			$this->host = $argHostname;
662		}
663		// Overwrites $this->host and $this->port if a port is specified.
664		$this->parseHostNameAndPort();
665
666		if ($argUsername != "") {
667			$this->user = $argUsername;
668		}
669		if ($argPassword != "") {
670			$this->password = 'not stored'; // not stored for security reasons
671		}
672		if ($argDatabaseName != "") {
673			$this->database = $argDatabaseName;
674		}
675
676		$this->_isPersistentConnection = false;
677
678		if ($forceNew) {
679			if ($rez=$this->_nconnect($this->host, $this->user, $argPassword, $this->database)) {
680				return true;
681			}
682		} else {
683			if ($rez=$this->_connect($this->host, $this->user, $argPassword, $this->database)) {
684				return true;
685			}
686		}
687		if (isset($rez)) {
688			$err = $this->ErrorMsg();
689			$errno = $this->ErrorNo();
690			if (empty($err)) {
691				$err = "Connection error to server '$argHostname' with user '$argUsername'";
692			}
693		} else {
694			$err = "Missing extension for ".$this->dataProvider;
695			$errno = 0;
696		}
697		if ($fn = $this->raiseErrorFn) {
698			$fn($this->databaseType, 'CONNECT', $errno, $err, $this->host, $this->database, $this);
699		}
700
701		$this->_connectionID = false;
702		if ($this->debug) {
703			ADOConnection::outp( $this->host.': '.$err);
704		}
705		return false;
706	}
707
708	function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName) {
709		return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName);
710	}
711
712
713	/**
714	 * Always force a new connection to database - currently only works with oracle
715	 *
716	 * @param [argHostname]		Host to connect to
717	 * @param [argUsername]		Userid to login
718	 * @param [argPassword]		Associated password
719	 * @param [argDatabaseName]	database
720	 *
721	 * @return true or false
722	 */
723	function NConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") {
724		return $this->Connect($argHostname, $argUsername, $argPassword, $argDatabaseName, true);
725	}
726
727	/**
728	 * Establish persistent connect to database
729	 *
730	 * @param [argHostname]		Host to connect to
731	 * @param [argUsername]		Userid to login
732	 * @param [argPassword]		Associated password
733	 * @param [argDatabaseName]	database
734	 *
735	 * @return return true or false
736	 */
737	function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") {
738
739		if (defined('ADODB_NEVER_PERSIST')) {
740			return $this->Connect($argHostname,$argUsername,$argPassword,$argDatabaseName);
741		}
742
743		if ($argHostname != "") {
744			$this->host = $argHostname;
745		}
746		// Overwrites $this->host and $this->port if a port is specified.
747		$this->parseHostNameAndPort();
748
749		if ($argUsername != "") {
750			$this->user = $argUsername;
751		}
752		if ($argPassword != "") {
753			$this->password = 'not stored';
754		}
755		if ($argDatabaseName != "") {
756			$this->database = $argDatabaseName;
757		}
758
759		$this->_isPersistentConnection = true;
760
761		if ($rez = $this->_pconnect($this->host, $this->user, $argPassword, $this->database)) {
762			return true;
763		}
764		if (isset($rez)) {
765			$err = $this->ErrorMsg();
766			if (empty($err)) {
767				$err = "Connection error to server '$argHostname' with user '$argUsername'";
768			}
769			$ret = false;
770		} else {
771			$err = "Missing extension for ".$this->dataProvider;
772			$ret = 0;
773		}
774		if ($fn = $this->raiseErrorFn) {
775			$fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
776		}
777
778		$this->_connectionID = false;
779		if ($this->debug) {
780			ADOConnection::outp( $this->host.': '.$err);
781		}
782		return $ret;
783	}
784
785	function outp_throw($msg,$src='WARN',$sql='') {
786		if (defined('ADODB_ERROR_HANDLER') &&  ADODB_ERROR_HANDLER == 'adodb_throw') {
787			adodb_throw($this->databaseType,$src,-9999,$msg,$sql,false,$this);
788			return;
789		}
790		ADOConnection::outp($msg);
791	}
792
793	// create cache class. Code is backward compat with old memcache implementation
794	function _CreateCache() {
795		global $ADODB_CACHE, $ADODB_CACHE_CLASS;
796
797		if ($this->memCache) {
798			global $ADODB_INCLUDED_MEMCACHE;
799
800			if (empty($ADODB_INCLUDED_MEMCACHE)) {
801				include_once(ADODB_DIR.'/adodb-memcache.lib.inc.php');
802			}
803			$ADODB_CACHE = new ADODB_Cache_MemCache($this);
804		} else {
805			$ADODB_CACHE = new $ADODB_CACHE_CLASS($this);
806		}
807	}
808
809	// Format date column in sql string given an input format that understands Y M D
810	function SQLDate($fmt, $col=false) {
811		if (!$col) {
812			$col = $this->sysDate;
813		}
814		return $col; // child class implement
815	}
816
817	/**
818	 * Should prepare the sql statement and return the stmt resource.
819	 * For databases that do not support this, we return the $sql. To ensure
820	 * compatibility with databases that do not support prepare:
821	 *
822	 *   $stmt = $db->Prepare("insert into table (id, name) values (?,?)");
823	 *   $db->Execute($stmt,array(1,'Jill')) or die('insert failed');
824	 *   $db->Execute($stmt,array(2,'Joe')) or die('insert failed');
825	 *
826	 * @param sql	SQL to send to database
827	 *
828	 * @return return FALSE, or the prepared statement, or the original sql if
829	 *         if the database does not support prepare.
830	 *
831	 */
832	function Prepare($sql) {
833		return $sql;
834	}
835
836	/**
837	 * Some databases, eg. mssql require a different function for preparing
838	 * stored procedures. So we cannot use Prepare().
839	 *
840	 * Should prepare the stored procedure  and return the stmt resource.
841	 * For databases that do not support this, we return the $sql. To ensure
842	 * compatibility with databases that do not support prepare:
843	 *
844	 * @param sql	SQL to send to database
845	 *
846	 * @return return FALSE, or the prepared statement, or the original sql if
847	 *         if the database does not support prepare.
848	 *
849	 */
850	function PrepareSP($sql,$param=true) {
851		return $this->Prepare($sql,$param);
852	}
853
854	/**
855	* PEAR DB Compat
856	*/
857	function Quote($s) {
858		return $this->qstr($s,false);
859	}
860
861	/**
862	 * Requested by "Karsten Dambekalns" <k.dambekalns@fishfarm.de>
863	 */
864	function QMagic($s) {
865		return $this->qstr($s,get_magic_quotes_gpc());
866	}
867
868	function q(&$s) {
869		//if (!empty($this->qNull && $s == 'null') {
870		//	return $s;
871		//}
872		$s = $this->qstr($s,false);
873	}
874
875	/**
876	* PEAR DB Compat - do not use internally.
877	*/
878	function ErrorNative() {
879		return $this->ErrorNo();
880	}
881
882
883	/**
884	 * PEAR DB Compat - do not use internally.
885	 */
886	function nextId($seq_name) {
887		return $this->GenID($seq_name);
888	}
889
890	/**
891	 * Lock a row, will escalate and lock the table if row locking not supported
892	 * will normally free the lock at the end of the transaction
893	 *
894	 * @param $table	name of table to lock
895	 * @param $where	where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock
896	 */
897	function RowLock($table,$where,$col='1 as adodbignore') {
898		return false;
899	}
900
901	function CommitLock($table) {
902		return $this->CommitTrans();
903	}
904
905	function RollbackLock($table) {
906		return $this->RollbackTrans();
907	}
908
909	/**
910	* PEAR DB Compat - do not use internally.
911	*
912	* The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical
913	* for easy porting :-)
914	*
915	* @param mode	The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM
916	* @returns		The previous fetch mode
917	*/
918	function SetFetchMode($mode) {
919		$old = $this->fetchMode;
920		$this->fetchMode = $mode;
921
922		if ($old === false) {
923			global $ADODB_FETCH_MODE;
924			return $ADODB_FETCH_MODE;
925		}
926		return $old;
927	}
928
929
930	/**
931	* PEAR DB Compat - do not use internally.
932	*/
933	function Query($sql, $inputarr=false) {
934		$rs = $this->Execute($sql, $inputarr);
935		if (!$rs && defined('ADODB_PEAR')) {
936			return ADODB_PEAR_Error();
937		}
938		return $rs;
939	}
940
941
942	/**
943	* PEAR DB Compat - do not use internally
944	*/
945	function LimitQuery($sql, $offset, $count, $params=false) {
946		$rs = $this->SelectLimit($sql, $count, $offset, $params);
947		if (!$rs && defined('ADODB_PEAR')) {
948			return ADODB_PEAR_Error();
949		}
950		return $rs;
951	}
952
953
954	/**
955	* PEAR DB Compat - do not use internally
956	*/
957	function Disconnect() {
958		return $this->Close();
959	}
960
961	/**
962	 * Returns a placeholder for query parameters
963	 * e.g. $DB->Param('a') will return
964	 * - '?' for most databases
965	 * - ':a' for Oracle
966	 * - '$1', '$2', etc. for PostgreSQL
967	 * @param string $name parameter's name, false to force a reset of the
968	 *                     number to 1 (for databases that require positioned
969	 *                     params such as PostgreSQL; note that ADOdb will
970	 *                     automatically reset this when executing a query )
971	 * @param string $type (unused)
972	 * @return string query parameter placeholder
973	 */
974	function Param($name,$type='C') {
975		return '?';
976	}
977
978	/*
979		InParameter and OutParameter are self-documenting versions of Parameter().
980	*/
981	function InParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false) {
982		return $this->Parameter($stmt,$var,$name,false,$maxLen,$type);
983	}
984
985	/*
986	*/
987	function OutParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false) {
988		return $this->Parameter($stmt,$var,$name,true,$maxLen,$type);
989
990	}
991
992
993	/*
994	Usage in oracle
995		$stmt = $db->Prepare('select * from table where id =:myid and group=:group');
996		$db->Parameter($stmt,$id,'myid');
997		$db->Parameter($stmt,$group,'group',64);
998		$db->Execute();
999
1000		@param $stmt Statement returned by Prepare() or PrepareSP().
1001		@param $var PHP variable to bind to
1002		@param $name Name of stored procedure variable name to bind to.
1003		@param [$isOutput] Indicates direction of parameter 0/false=IN  1=OUT  2= IN/OUT. This is ignored in oci8.
1004		@param [$maxLen] Holds an maximum length of the variable.
1005		@param [$type] The data type of $var. Legal values depend on driver.
1006
1007	*/
1008	function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false) {
1009		return false;
1010	}
1011
1012
1013	function IgnoreErrors($saveErrs=false) {
1014		if (!$saveErrs) {
1015			$saveErrs = array($this->raiseErrorFn,$this->_transOK);
1016			$this->raiseErrorFn = false;
1017			return $saveErrs;
1018		} else {
1019			$this->raiseErrorFn = $saveErrs[0];
1020			$this->_transOK = $saveErrs[1];
1021		}
1022	}
1023
1024	/**
1025	 * Improved method of initiating a transaction. Used together with CompleteTrans().
1026	 * Advantages include:
1027     *
1028	 * a. StartTrans/CompleteTrans is nestable, unlike BeginTrans/CommitTrans/RollbackTrans.
1029	 *    Only the outermost block is treated as a transaction.<br>
1030	 * b. CompleteTrans auto-detects SQL errors, and will rollback on errors, commit otherwise.<br>
1031	 * c. All BeginTrans/CommitTrans/RollbackTrans inside a StartTrans/CompleteTrans block
1032	 *    are disabled, making it backward compatible.
1033	 */
1034	function StartTrans($errfn = 'ADODB_TransMonitor') {
1035		if ($this->transOff > 0) {
1036			$this->transOff += 1;
1037			return true;
1038		}
1039
1040		$this->_oldRaiseFn = $this->raiseErrorFn;
1041		$this->raiseErrorFn = $errfn;
1042		$this->_transOK = true;
1043
1044		if ($this->debug && $this->transCnt > 0) {
1045			ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans");
1046		}
1047		$ok = $this->BeginTrans();
1048		$this->transOff = 1;
1049		return $ok;
1050	}
1051
1052
1053	/**
1054		Used together with StartTrans() to end a transaction. Monitors connection
1055		for sql errors, and will commit or rollback as appropriate.
1056
1057		@autoComplete if true, monitor sql errors and commit and rollback as appropriate,
1058		and if set to false force rollback even if no SQL error detected.
1059		@returns true on commit, false on rollback.
1060	*/
1061	function CompleteTrans($autoComplete = true) {
1062		if ($this->transOff > 1) {
1063			$this->transOff -= 1;
1064			return true;
1065		}
1066		$this->raiseErrorFn = $this->_oldRaiseFn;
1067
1068		$this->transOff = 0;
1069		if ($this->_transOK && $autoComplete) {
1070			if (!$this->CommitTrans()) {
1071				$this->_transOK = false;
1072				if ($this->debug) {
1073					ADOConnection::outp("Smart Commit failed");
1074				}
1075			} else {
1076				if ($this->debug) {
1077					ADOConnection::outp("Smart Commit occurred");
1078				}
1079			}
1080		} else {
1081			$this->_transOK = false;
1082			$this->RollbackTrans();
1083			if ($this->debug) {
1084				ADOCOnnection::outp("Smart Rollback occurred");
1085			}
1086		}
1087
1088		return $this->_transOK;
1089	}
1090
1091	/*
1092		At the end of a StartTrans/CompleteTrans block, perform a rollback.
1093	*/
1094	function FailTrans() {
1095		if ($this->debug)
1096			if ($this->transOff == 0) {
1097				ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans");
1098			} else {
1099				ADOConnection::outp("FailTrans was called");
1100				adodb_backtrace();
1101			}
1102		$this->_transOK = false;
1103	}
1104
1105	/**
1106		Check if transaction has failed, only for Smart Transactions.
1107	*/
1108	function HasFailedTrans() {
1109		if ($this->transOff > 0) {
1110			return $this->_transOK == false;
1111		}
1112		return false;
1113	}
1114
1115	/**
1116	 * Execute SQL
1117	 *
1118	 * @param sql		SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text)
1119	 * @param [inputarr]	holds the input data to bind to. Null elements will be set to null.
1120	 * @return RecordSet or false
1121	 */
1122	function Execute($sql,$inputarr=false) {
1123		if ($this->fnExecute) {
1124			$fn = $this->fnExecute;
1125			$ret = $fn($this,$sql,$inputarr);
1126			if (isset($ret)) {
1127				return $ret;
1128			}
1129		}
1130		if ($inputarr !== false) {
1131			if (!is_array($inputarr)) {
1132				$inputarr = array($inputarr);
1133			}
1134
1135			$element0 = reset($inputarr);
1136			# is_object check because oci8 descriptors can be passed in
1137			$array_2d = $this->bulkBind && is_array($element0) && !is_object(reset($element0));
1138
1139			//remove extra memory copy of input -mikefedyk
1140			unset($element0);
1141
1142			if (!is_array($sql) && !$this->_bindInputArray) {
1143				// @TODO this would consider a '?' within a string as a parameter...
1144				$sqlarr = explode('?',$sql);
1145				$nparams = sizeof($sqlarr)-1;
1146
1147				if (!$array_2d) {
1148					// When not Bind Bulk - convert to array of arguments list
1149					$inputarr = array($inputarr);
1150				} else {
1151					// Bulk bind - Make sure all list of params have the same number of elements
1152					$countElements = array_map('count', $inputarr);
1153					if (1 != count(array_unique($countElements))) {
1154						$this->outp_throw(
1155							"[bulk execute] Input array has different number of params  [" . print_r($countElements, true) .  "].",
1156							'Execute'
1157						);
1158						return false;
1159					}
1160					unset($countElements);
1161				}
1162				// Make sure the number of parameters provided in the input
1163				// array matches what the query expects
1164				$element0 = reset($inputarr);
1165				if ($nparams != count($element0)) {
1166					$this->outp_throw(
1167						"Input array has " . count($element0) .
1168						" params, does not match query: '" . htmlspecialchars($sql) . "'",
1169						'Execute'
1170					);
1171					return false;
1172				}
1173
1174				// clean memory
1175				unset($element0);
1176
1177				foreach($inputarr as $arr) {
1178					$sql = ''; $i = 0;
1179					foreach ($arr as $v) {
1180						$sql .= $sqlarr[$i];
1181						// from Ron Baldwin <ron.baldwin#sourceprose.com>
1182						// Only quote string types
1183						$typ = gettype($v);
1184						if ($typ == 'string') {
1185							//New memory copy of input created here -mikefedyk
1186							$sql .= $this->qstr($v);
1187						} else if ($typ == 'double') {
1188							$sql .= str_replace(',','.',$v); // locales fix so 1.1 does not get converted to 1,1
1189						} else if ($typ == 'boolean') {
1190							$sql .= $v ? $this->true : $this->false;
1191						} else if ($typ == 'object') {
1192							if (method_exists($v, '__toString')) {
1193								$sql .= $this->qstr($v->__toString());
1194							} else {
1195								$sql .= $this->qstr((string) $v);
1196							}
1197						} else if ($v === null) {
1198							$sql .= 'NULL';
1199						} else {
1200							$sql .= $v;
1201						}
1202						$i += 1;
1203
1204						if ($i == $nparams) {
1205							break;
1206						}
1207					} // while
1208					if (isset($sqlarr[$i])) {
1209						$sql .= $sqlarr[$i];
1210						if ($i+1 != sizeof($sqlarr)) {
1211							$this->outp_throw( "Input Array does not match ?: ".htmlspecialchars($sql),'Execute');
1212						}
1213					} else if ($i != sizeof($sqlarr)) {
1214						$this->outp_throw( "Input array does not match ?: ".htmlspecialchars($sql),'Execute');
1215					}
1216
1217					$ret = $this->_Execute($sql);
1218					if (!$ret) {
1219						return $ret;
1220					}
1221				}
1222			} else {
1223				if ($array_2d) {
1224					if (is_string($sql)) {
1225						$stmt = $this->Prepare($sql);
1226					} else {
1227						$stmt = $sql;
1228					}
1229
1230					foreach($inputarr as $arr) {
1231						$ret = $this->_Execute($stmt,$arr);
1232						if (!$ret) {
1233							return $ret;
1234						}
1235					}
1236				} else {
1237					$ret = $this->_Execute($sql,$inputarr);
1238				}
1239			}
1240		} else {
1241			$ret = $this->_Execute($sql,false);
1242		}
1243
1244		return $ret;
1245	}
1246
1247	function _Execute($sql,$inputarr=false) {
1248		// ExecuteCursor() may send non-string queries (such as arrays),
1249		// so we need to ignore those.
1250		if( is_string($sql) ) {
1251			// Strips keyword used to help generate SELECT COUNT(*) queries
1252			// from SQL if it exists.
1253			$sql = ADODB_str_replace( '_ADODB_COUNT', '', $sql );
1254		}
1255
1256		if ($this->debug) {
1257			global $ADODB_INCLUDED_LIB;
1258			if (empty($ADODB_INCLUDED_LIB)) {
1259				include(ADODB_DIR.'/adodb-lib.inc.php');
1260			}
1261			$this->_queryID = _adodb_debug_execute($this, $sql,$inputarr);
1262		} else {
1263			$this->_queryID = @$this->_query($sql,$inputarr);
1264		}
1265
1266		// ************************
1267		// OK, query executed
1268		// ************************
1269
1270		// error handling if query fails
1271		if ($this->_queryID === false) {
1272			if ($this->debug == 99) {
1273				adodb_backtrace(true,5);
1274			}
1275			$fn = $this->raiseErrorFn;
1276			if ($fn) {
1277				$fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr,$this);
1278			}
1279			return false;
1280		}
1281
1282		// return simplified recordset for inserts/updates/deletes with lower overhead
1283		if ($this->_queryID === true) {
1284			$rsclass = $this->rsPrefix.'empty';
1285			$rs = (class_exists($rsclass)) ? new $rsclass():  new ADORecordSet_empty();
1286
1287			return $rs;
1288		}
1289
1290		// return real recordset from select statement
1291		$rsclass = $this->rsPrefix.$this->databaseType;
1292		$rs = new $rsclass($this->_queryID,$this->fetchMode);
1293		$rs->connection = $this; // Pablo suggestion
1294		$rs->Init();
1295		if (is_array($sql)) {
1296			$rs->sql = $sql[0];
1297		} else {
1298			$rs->sql = $sql;
1299		}
1300		if ($rs->_numOfRows <= 0) {
1301			global $ADODB_COUNTRECS;
1302			if ($ADODB_COUNTRECS) {
1303				if (!$rs->EOF) {
1304					$rs = $this->_rs2rs($rs,-1,-1,!is_array($sql));
1305					$rs->_queryID = $this->_queryID;
1306				} else
1307					$rs->_numOfRows = 0;
1308			}
1309		}
1310		return $rs;
1311	}
1312
1313	function CreateSequence($seqname='adodbseq',$startID=1) {
1314		if (empty($this->_genSeqSQL)) {
1315			return false;
1316		}
1317		return $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
1318	}
1319
1320	function DropSequence($seqname='adodbseq') {
1321		if (empty($this->_dropSeqSQL)) {
1322			return false;
1323		}
1324		return $this->Execute(sprintf($this->_dropSeqSQL,$seqname));
1325	}
1326
1327	/**
1328	 * Generates a sequence id and stores it in $this->genID;
1329	 * GenID is only available if $this->hasGenID = true;
1330	 *
1331	 * @param seqname		name of sequence to use
1332	 * @param startID		if sequence does not exist, start at this ID
1333	 * @return		0 if not supported, otherwise a sequence id
1334	 */
1335	function GenID($seqname='adodbseq',$startID=1) {
1336		if (!$this->hasGenID) {
1337			return 0; // formerly returns false pre 1.60
1338		}
1339
1340		$getnext = sprintf($this->_genIDSQL,$seqname);
1341
1342		$holdtransOK = $this->_transOK;
1343
1344		$save_handler = $this->raiseErrorFn;
1345		$this->raiseErrorFn = '';
1346		@($rs = $this->Execute($getnext));
1347		$this->raiseErrorFn = $save_handler;
1348
1349		if (!$rs) {
1350			$this->_transOK = $holdtransOK; //if the status was ok before reset
1351			$createseq = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
1352			$rs = $this->Execute($getnext);
1353		}
1354		if ($rs && !$rs->EOF) {
1355			$this->genID = reset($rs->fields);
1356		} else {
1357			$this->genID = 0; // false
1358		}
1359
1360		if ($rs) {
1361			$rs->Close();
1362		}
1363
1364		return $this->genID;
1365	}
1366
1367	/**
1368	 * @param $table string name of the table, not needed by all databases (eg. mysql), default ''
1369	 * @param $column string name of the column, not needed by all databases (eg. mysql), default ''
1370	 * @return  the last inserted ID. Not all databases support this.
1371	 */
1372	function Insert_ID($table='',$column='') {
1373		if ($this->_logsql && $this->lastInsID) {
1374			return $this->lastInsID;
1375		}
1376		if ($this->hasInsertID) {
1377			return $this->_insertid($table,$column);
1378		}
1379		if ($this->debug) {
1380			ADOConnection::outp( '<p>Insert_ID error</p>');
1381			adodb_backtrace();
1382		}
1383		return false;
1384	}
1385
1386
1387	/**
1388	 * Portable Insert ID. Pablo Roca <pabloroca#mvps.org>
1389	 *
1390	 * @return  the last inserted ID. All databases support this. But aware possible
1391	 * problems in multiuser environments. Heavy test this before deploying.
1392	 */
1393	function PO_Insert_ID($table="", $id="") {
1394		if ($this->hasInsertID){
1395			return $this->Insert_ID($table,$id);
1396		} else {
1397			return $this->GetOne("SELECT MAX($id) FROM $table");
1398		}
1399	}
1400
1401	/**
1402	* @return # rows affected by UPDATE/DELETE
1403	*/
1404	function Affected_Rows() {
1405		if ($this->hasAffectedRows) {
1406			if ($this->fnExecute === 'adodb_log_sql') {
1407				if ($this->_logsql && $this->_affected !== false) {
1408					return $this->_affected;
1409				}
1410			}
1411			$val = $this->_affectedrows();
1412			return ($val < 0) ? false : $val;
1413		}
1414
1415		if ($this->debug) {
1416			ADOConnection::outp( '<p>Affected_Rows error</p>',false);
1417		}
1418		return false;
1419	}
1420
1421
1422	/**
1423	 * @return  the last error message
1424	 */
1425	function ErrorMsg() {
1426		if ($this->_errorMsg) {
1427			return '!! '.strtoupper($this->dataProvider.' '.$this->databaseType).': '.$this->_errorMsg;
1428		} else {
1429			return '';
1430		}
1431	}
1432
1433
1434	/**
1435	 * @return the last error number. Normally 0 means no error.
1436	 */
1437	function ErrorNo() {
1438		return ($this->_errorMsg) ? -1 : 0;
1439	}
1440
1441	function MetaError($err=false) {
1442		include_once(ADODB_DIR."/adodb-error.inc.php");
1443		if ($err === false) {
1444			$err = $this->ErrorNo();
1445		}
1446		return adodb_error($this->dataProvider,$this->databaseType,$err);
1447	}
1448
1449	function MetaErrorMsg($errno) {
1450		include_once(ADODB_DIR."/adodb-error.inc.php");
1451		return adodb_errormsg($errno);
1452	}
1453
1454	/**
1455	 * @returns an array with the primary key columns in it.
1456	 */
1457	function MetaPrimaryKeys($table, $owner=false) {
1458	// owner not used in base class - see oci8
1459		$p = array();
1460		$objs = $this->MetaColumns($table);
1461		if ($objs) {
1462			foreach($objs as $v) {
1463				if (!empty($v->primary_key)) {
1464					$p[] = $v->name;
1465				}
1466			}
1467		}
1468		if (sizeof($p)) {
1469			return $p;
1470		}
1471		if (function_exists('ADODB_VIEW_PRIMARYKEYS')) {
1472			return ADODB_VIEW_PRIMARYKEYS($this->databaseType, $this->database, $table, $owner);
1473		}
1474		return false;
1475	}
1476
1477	/**
1478	 * @returns assoc array where keys are tables, and values are foreign keys
1479	 */
1480	function MetaForeignKeys($table, $owner=false, $upper=false) {
1481		return false;
1482	}
1483	/**
1484	 * Choose a database to connect to. Many databases do not support this.
1485	 *
1486	 * @param dbName is the name of the database to select
1487	 * @return true or false
1488	 */
1489	function SelectDB($dbName) {return false;}
1490
1491
1492	/**
1493	 * Will select, getting rows from $offset (1-based), for $nrows.
1494	 * This simulates the MySQL "select * from table limit $offset,$nrows" , and
1495	 * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
1496	 * MySQL and PostgreSQL parameter ordering is the opposite of the other.
1497	 * eg.
1498	 *  SelectLimit('select * from table',3); will return rows 1 to 3 (1-based)
1499	 *  SelectLimit('select * from table',3,2); will return rows 3 to 5 (1-based)
1500	 *
1501	 * Uses SELECT TOP for Microsoft databases (when $this->hasTop is set)
1502	 * BUG: Currently SelectLimit fails with $sql with LIMIT or TOP clause already set
1503	 *
1504	 * @param sql
1505	 * @param [offset]	is the row to start calculations from (1-based)
1506	 * @param [nrows]		is the number of rows to get
1507	 * @param [inputarr]	array of bind variables
1508	 * @param [secs2cache]		is a private parameter only used by jlim
1509	 * @return		the recordset ($rs->databaseType == 'array')
1510	 */
1511	function SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0) {
1512		$nrows = (int)$nrows;
1513		$offset = (int)$offset;
1514
1515		if ($this->hasTop && $nrows > 0) {
1516			// suggested by Reinhard Balling. Access requires top after distinct
1517			// Informix requires first before distinct - F Riosa
1518			$ismssql = (strpos($this->databaseType,'mssql') !== false);
1519			if ($ismssql) {
1520				$isaccess = false;
1521			} else {
1522				$isaccess = (strpos($this->databaseType,'access') !== false);
1523			}
1524
1525			if ($offset <= 0) {
1526					// access includes ties in result
1527					if ($isaccess) {
1528						$sql = preg_replace(
1529						'/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nrows.' ',$sql);
1530
1531						if ($secs2cache != 0) {
1532							$ret = $this->CacheExecute($secs2cache, $sql,$inputarr);
1533						} else {
1534							$ret = $this->Execute($sql,$inputarr);
1535						}
1536						return $ret; // PHP5 fix
1537					} else if ($ismssql){
1538						$sql = preg_replace(
1539						'/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nrows.' ',$sql);
1540					} else {
1541						$sql = preg_replace(
1542						'/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.$nrows.' ',$sql);
1543					}
1544			} else {
1545				$nn = $nrows + $offset;
1546				if ($isaccess || $ismssql) {
1547					$sql = preg_replace(
1548					'/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql);
1549				} else {
1550					$sql = preg_replace(
1551					'/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql);
1552				}
1553			}
1554		}
1555
1556		// if $offset>0, we want to skip rows, and $ADODB_COUNTRECS is set, we buffer  rows
1557		// 0 to offset-1 which will be discarded anyway. So we disable $ADODB_COUNTRECS.
1558		global $ADODB_COUNTRECS;
1559
1560		$savec = $ADODB_COUNTRECS;
1561		$ADODB_COUNTRECS = false;
1562
1563
1564		if ($secs2cache != 0) {
1565			$rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
1566		} else {
1567			$rs = $this->Execute($sql,$inputarr);
1568		}
1569
1570		$ADODB_COUNTRECS = $savec;
1571		if ($rs && !$rs->EOF) {
1572			$rs = $this->_rs2rs($rs,$nrows,$offset);
1573		}
1574		//print_r($rs);
1575		return $rs;
1576	}
1577
1578	/**
1579	* Create serializable recordset. Breaks rs link to connection.
1580	*
1581	* @param rs			the recordset to serialize
1582	*/
1583	function SerializableRS(&$rs) {
1584		$rs2 = $this->_rs2rs($rs);
1585		$ignore = false;
1586		$rs2->connection = $ignore;
1587
1588		return $rs2;
1589	}
1590
1591	/**
1592	* Convert database recordset to an array recordset
1593	* input recordset's cursor should be at beginning, and
1594	* old $rs will be closed.
1595	*
1596	* @param rs			the recordset to copy
1597	* @param [nrows]	number of rows to retrieve (optional)
1598	* @param [offset]	offset by number of rows (optional)
1599	* @return			the new recordset
1600	*/
1601	function &_rs2rs(&$rs,$nrows=-1,$offset=-1,$close=true) {
1602		if (! $rs) {
1603			return false;
1604		}
1605		$dbtype = $rs->databaseType;
1606		if (!$dbtype) {
1607			$rs = $rs;  // required to prevent crashing in 4.2.1, but does not happen in 4.3.1 -- why ?
1608			return $rs;
1609		}
1610		if (($dbtype == 'array' || $dbtype == 'csv') && $nrows == -1 && $offset == -1) {
1611			$rs->MoveFirst();
1612			$rs = $rs; // required to prevent crashing in 4.2.1, but does not happen in 4.3.1-- why ?
1613			return $rs;
1614		}
1615		$flds = array();
1616		for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) {
1617			$flds[] = $rs->FetchField($i);
1618		}
1619
1620		$arr = $rs->GetArrayLimit($nrows,$offset);
1621		//print_r($arr);
1622		if ($close) {
1623			$rs->Close();
1624		}
1625
1626		$arrayClass = $this->arrayClass;
1627
1628		$rs2 = new $arrayClass();
1629		$rs2->connection = $this;
1630		$rs2->sql = $rs->sql;
1631		$rs2->dataProvider = $this->dataProvider;
1632		$rs2->InitArrayFields($arr,$flds);
1633		$rs2->fetchMode = isset($rs->adodbFetchMode) ? $rs->adodbFetchMode : $rs->fetchMode;
1634		return $rs2;
1635	}
1636
1637	/*
1638	* Return all rows. Compat with PEAR DB
1639	*/
1640	function GetAll($sql, $inputarr=false) {
1641		$arr = $this->GetArray($sql,$inputarr);
1642		return $arr;
1643	}
1644
1645	function GetAssoc($sql, $inputarr=false,$force_array = false, $first2cols = false) {
1646		$rs = $this->Execute($sql, $inputarr);
1647		if (!$rs) {
1648			return false;
1649		}
1650		$arr = $rs->GetAssoc($force_array,$first2cols);
1651		return $arr;
1652	}
1653
1654	function CacheGetAssoc($secs2cache, $sql=false, $inputarr=false,$force_array = false, $first2cols = false) {
1655		if (!is_numeric($secs2cache)) {
1656			$first2cols = $force_array;
1657			$force_array = $inputarr;
1658		}
1659		$rs = $this->CacheExecute($secs2cache, $sql, $inputarr);
1660		if (!$rs) {
1661			return false;
1662		}
1663		$arr = $rs->GetAssoc($force_array,$first2cols);
1664		return $arr;
1665	}
1666
1667	/**
1668	* Return first element of first row of sql statement. Recordset is disposed
1669	* for you.
1670	*
1671	* @param sql			SQL statement
1672	* @param [inputarr]		input bind array
1673	*/
1674	function GetOne($sql,$inputarr=false) {
1675		global $ADODB_COUNTRECS,$ADODB_GETONE_EOF;
1676
1677		$crecs = $ADODB_COUNTRECS;
1678		$ADODB_COUNTRECS = false;
1679
1680		$ret = false;
1681		$rs = $this->Execute($sql,$inputarr);
1682		if ($rs) {
1683			if ($rs->EOF) {
1684				$ret = $ADODB_GETONE_EOF;
1685			} else {
1686				$ret = reset($rs->fields);
1687			}
1688
1689			$rs->Close();
1690		}
1691		$ADODB_COUNTRECS = $crecs;
1692		return $ret;
1693	}
1694
1695	// $where should include 'WHERE fld=value'
1696	function GetMedian($table, $field,$where = '') {
1697		$total = $this->GetOne("select count(*) from $table $where");
1698		if (!$total) {
1699			return false;
1700		}
1701
1702		$midrow = (integer) ($total/2);
1703		$rs = $this->SelectLimit("select $field from $table $where order by 1",1,$midrow);
1704		if ($rs && !$rs->EOF) {
1705			return reset($rs->fields);
1706		}
1707		return false;
1708	}
1709
1710
1711	function CacheGetOne($secs2cache,$sql=false,$inputarr=false) {
1712		global $ADODB_GETONE_EOF;
1713
1714		$ret = false;
1715		$rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
1716		if ($rs) {
1717			if ($rs->EOF) {
1718				$ret = $ADODB_GETONE_EOF;
1719			} else {
1720				$ret = reset($rs->fields);
1721			}
1722			$rs->Close();
1723		}
1724
1725		return $ret;
1726	}
1727
1728	function GetCol($sql, $inputarr = false, $trim = false) {
1729
1730		$rs = $this->Execute($sql, $inputarr);
1731		if ($rs) {
1732			$rv = array();
1733			if ($trim) {
1734				while (!$rs->EOF) {
1735					$rv[] = trim(reset($rs->fields));
1736					$rs->MoveNext();
1737				}
1738			} else {
1739				while (!$rs->EOF) {
1740					$rv[] = reset($rs->fields);
1741					$rs->MoveNext();
1742				}
1743			}
1744			$rs->Close();
1745		} else {
1746			$rv = false;
1747		}
1748		return $rv;
1749	}
1750
1751	function CacheGetCol($secs, $sql = false, $inputarr = false,$trim=false) {
1752		$rs = $this->CacheExecute($secs, $sql, $inputarr);
1753		if ($rs) {
1754			$rv = array();
1755			if ($trim) {
1756				while (!$rs->EOF) {
1757					$rv[] = trim(reset($rs->fields));
1758					$rs->MoveNext();
1759				}
1760			} else {
1761				while (!$rs->EOF) {
1762					$rv[] = reset($rs->fields);
1763					$rs->MoveNext();
1764				}
1765			}
1766			$rs->Close();
1767		} else
1768			$rv = false;
1769
1770		return $rv;
1771	}
1772
1773	function Transpose(&$rs,$addfieldnames=true) {
1774		$rs2 = $this->_rs2rs($rs);
1775		if (!$rs2) {
1776			return false;
1777		}
1778
1779		$rs2->_transpose($addfieldnames);
1780		return $rs2;
1781	}
1782
1783	/*
1784		Calculate the offset of a date for a particular database and generate
1785			appropriate SQL. Useful for calculating future/past dates and storing
1786			in a database.
1787
1788		If dayFraction=1.5 means 1.5 days from now, 1.0/24 for 1 hour.
1789	*/
1790	function OffsetDate($dayFraction,$date=false) {
1791		if (!$date) {
1792			$date = $this->sysDate;
1793		}
1794		return  '('.$date.'+'.$dayFraction.')';
1795	}
1796
1797
1798	/**
1799	*
1800	* @param sql			SQL statement
1801	* @param [inputarr]		input bind array
1802	*/
1803	function GetArray($sql,$inputarr=false) {
1804		global $ADODB_COUNTRECS;
1805
1806		$savec = $ADODB_COUNTRECS;
1807		$ADODB_COUNTRECS = false;
1808		$rs = $this->Execute($sql,$inputarr);
1809		$ADODB_COUNTRECS = $savec;
1810		if (!$rs)
1811			if (defined('ADODB_PEAR')) {
1812				$cls = ADODB_PEAR_Error();
1813				return $cls;
1814			} else {
1815				return false;
1816			}
1817		$arr = $rs->GetArray();
1818		$rs->Close();
1819		return $arr;
1820	}
1821
1822	function CacheGetAll($secs2cache,$sql=false,$inputarr=false) {
1823		$arr = $this->CacheGetArray($secs2cache,$sql,$inputarr);
1824		return $arr;
1825	}
1826
1827	function CacheGetArray($secs2cache,$sql=false,$inputarr=false) {
1828		global $ADODB_COUNTRECS;
1829
1830		$savec = $ADODB_COUNTRECS;
1831		$ADODB_COUNTRECS = false;
1832		$rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
1833		$ADODB_COUNTRECS = $savec;
1834
1835		if (!$rs)
1836			if (defined('ADODB_PEAR')) {
1837				$cls = ADODB_PEAR_Error();
1838				return $cls;
1839			} else {
1840				return false;
1841			}
1842		$arr = $rs->GetArray();
1843		$rs->Close();
1844		return $arr;
1845	}
1846
1847	function GetRandRow($sql, $arr= false) {
1848		$rezarr = $this->GetAll($sql, $arr);
1849		$sz = sizeof($rezarr);
1850		return $rezarr[abs(rand()) % $sz];
1851	}
1852
1853	/**
1854	* Return one row of sql statement. Recordset is disposed for you.
1855	* Note that SelectLimit should not be called.
1856	*
1857	* @param sql			SQL statement
1858	* @param [inputarr]		input bind array
1859	*/
1860	function GetRow($sql,$inputarr=false) {
1861		global $ADODB_COUNTRECS;
1862
1863		$crecs = $ADODB_COUNTRECS;
1864		$ADODB_COUNTRECS = false;
1865
1866		$rs = $this->Execute($sql,$inputarr);
1867
1868		$ADODB_COUNTRECS = $crecs;
1869		if ($rs) {
1870			if (!$rs->EOF) {
1871				$arr = $rs->fields;
1872			} else {
1873				$arr = array();
1874			}
1875			$rs->Close();
1876			return $arr;
1877		}
1878
1879		return false;
1880	}
1881
1882	function CacheGetRow($secs2cache,$sql=false,$inputarr=false) {
1883		$rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
1884		if ($rs) {
1885			if (!$rs->EOF) {
1886				$arr = $rs->fields;
1887			} else {
1888				$arr = array();
1889			}
1890
1891			$rs->Close();
1892			return $arr;
1893		}
1894		return false;
1895	}
1896
1897	/**
1898	* Insert or replace a single record. Note: this is not the same as MySQL's replace.
1899	* ADOdb's Replace() uses update-insert semantics, not insert-delete-duplicates of MySQL.
1900	* Also note that no table locking is done currently, so it is possible that the
1901	* record be inserted twice by two programs...
1902	*
1903	* $this->Replace('products', array('prodname' =>"'Nails'","price" => 3.99), 'prodname');
1904	*
1905	* $table		table name
1906	* $fieldArray	associative array of data (you must quote strings yourself).
1907	* $keyCol		the primary key field name or if compound key, array of field names
1908	* autoQuote		set to true to use a hueristic to quote strings. Works with nulls and numbers
1909	*					but does not work with dates nor SQL functions.
1910	* has_autoinc	the primary key is an auto-inc field, so skip in insert.
1911	*
1912	* Currently blob replace not supported
1913	*
1914	* returns 0 = fail, 1 = update, 2 = insert
1915	*/
1916
1917	function Replace($table, $fieldArray, $keyCol, $autoQuote=false, $has_autoinc=false) {
1918		global $ADODB_INCLUDED_LIB;
1919		if (empty($ADODB_INCLUDED_LIB)) {
1920			include(ADODB_DIR.'/adodb-lib.inc.php');
1921		}
1922
1923		return _adodb_replace($this, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc);
1924	}
1925
1926
1927	/**
1928	 * Will select, getting rows from $offset (1-based), for $nrows.
1929	 * This simulates the MySQL "select * from table limit $offset,$nrows" , and
1930	 * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
1931	 * MySQL and PostgreSQL parameter ordering is the opposite of the other.
1932	 * eg.
1933	 *  CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based)
1934	 *  CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based)
1935	 *
1936	 * BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set
1937	 *
1938	 * @param [secs2cache]	seconds to cache data, set to 0 to force query. This is optional
1939	 * @param sql
1940	 * @param [offset]	is the row to start calculations from (1-based)
1941	 * @param [nrows]	is the number of rows to get
1942	 * @param [inputarr]	array of bind variables
1943	 * @return		the recordset ($rs->databaseType == 'array')
1944	 */
1945	function CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false) {
1946		if (!is_numeric($secs2cache)) {
1947			if ($sql === false) {
1948				$sql = -1;
1949			}
1950			if ($offset == -1) {
1951				$offset = false;
1952			}
1953												// sql,	nrows, offset,inputarr
1954			$rs = $this->SelectLimit($secs2cache,$sql,$nrows,$offset,$this->cacheSecs);
1955		} else {
1956			if ($sql === false) {
1957				$this->outp_throw("Warning: \$sql missing from CacheSelectLimit()",'CacheSelectLimit');
1958			}
1959			$rs = $this->SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
1960		}
1961		return $rs;
1962	}
1963
1964	/**
1965	 * Flush cached recordsets that match a particular $sql statement.
1966	 * If $sql == false, then we purge all files in the cache.
1967	 */
1968	function CacheFlush($sql=false,$inputarr=false) {
1969		global $ADODB_CACHE_DIR, $ADODB_CACHE;
1970
1971		# Create cache if it does not exist
1972		if (empty($ADODB_CACHE)) {
1973			$this->_CreateCache();
1974		}
1975
1976		if (!$sql) {
1977			$ADODB_CACHE->flushall($this->debug);
1978			return;
1979		}
1980
1981		$f = $this->_gencachename($sql.serialize($inputarr),false);
1982		return $ADODB_CACHE->flushcache($f, $this->debug);
1983	}
1984
1985
1986	/**
1987	 * Private function to generate filename for caching.
1988	 * Filename is generated based on:
1989	 *
1990	 *  - sql statement
1991	 *  - database type (oci8, ibase, ifx, etc)
1992	 *  - database name
1993	 *  - userid
1994	 *  - setFetchMode (adodb 4.23)
1995	 *
1996	 * When not in safe mode, we create 256 sub-directories in the cache directory ($ADODB_CACHE_DIR).
1997	 * Assuming that we can have 50,000 files per directory with good performance,
1998	 * then we can scale to 12.8 million unique cached recordsets. Wow!
1999	 */
2000	function _gencachename($sql,$createdir) {
2001		global $ADODB_CACHE, $ADODB_CACHE_DIR;
2002
2003		if ($this->fetchMode === false) {
2004			global $ADODB_FETCH_MODE;
2005			$mode = $ADODB_FETCH_MODE;
2006		} else {
2007			$mode = $this->fetchMode;
2008		}
2009		$m = md5($sql.$this->databaseType.$this->database.$this->user.$mode);
2010		if (!$ADODB_CACHE->createdir) {
2011			return $m;
2012		}
2013		if (!$createdir) {
2014			$dir = $ADODB_CACHE->getdirname($m);
2015		} else {
2016			$dir = $ADODB_CACHE->createdir($m, $this->debug);
2017		}
2018
2019		return $dir.'/adodb_'.$m.'.cache';
2020	}
2021
2022
2023	/**
2024	 * Execute SQL, caching recordsets.
2025	 *
2026	 * @param [secs2cache]	seconds to cache data, set to 0 to force query.
2027	 *					  This is an optional parameter.
2028	 * @param sql		SQL statement to execute
2029	 * @param [inputarr]	holds the input data  to bind to
2030	 * @return		RecordSet or false
2031	 */
2032	function CacheExecute($secs2cache,$sql=false,$inputarr=false) {
2033		global $ADODB_CACHE;
2034
2035		if (empty($ADODB_CACHE)) {
2036			$this->_CreateCache();
2037		}
2038
2039		if (!is_numeric($secs2cache)) {
2040			$inputarr = $sql;
2041			$sql = $secs2cache;
2042			$secs2cache = $this->cacheSecs;
2043		}
2044
2045		if (is_array($sql)) {
2046			$sqlparam = $sql;
2047			$sql = $sql[0];
2048		} else
2049			$sqlparam = $sql;
2050
2051
2052		$md5file = $this->_gencachename($sql.serialize($inputarr),true);
2053		$err = '';
2054
2055		if ($secs2cache > 0){
2056			$rs = $ADODB_CACHE->readcache($md5file,$err,$secs2cache,$this->arrayClass);
2057			$this->numCacheHits += 1;
2058		} else {
2059			$err='Timeout 1';
2060			$rs = false;
2061			$this->numCacheMisses += 1;
2062		}
2063
2064		if (!$rs) {
2065		// no cached rs found
2066			if ($this->debug) {
2067				if (get_magic_quotes_runtime() && !$this->memCache) {
2068					ADOConnection::outp("Please disable magic_quotes_runtime - it corrupts cache files :(");
2069				}
2070				if ($this->debug !== -1) {
2071					ADOConnection::outp( " $md5file cache failure: $err (this is a notice and not an error)");
2072				}
2073			}
2074
2075			$rs = $this->Execute($sqlparam,$inputarr);
2076
2077			if ($rs) {
2078				$eof = $rs->EOF;
2079				$rs = $this->_rs2rs($rs); // read entire recordset into memory immediately
2080				$rs->timeCreated = time(); // used by caching
2081				$txt = _rs2serialize($rs,false,$sql); // serialize
2082
2083				$ok = $ADODB_CACHE->writecache($md5file,$txt,$this->debug, $secs2cache);
2084				if (!$ok) {
2085					if ($ok === false) {
2086						$em = 'Cache write error';
2087						$en = -32000;
2088
2089						if ($fn = $this->raiseErrorFn) {
2090							$fn($this->databaseType,'CacheExecute', $en, $em, $md5file,$sql,$this);
2091						}
2092					} else {
2093						$em = 'Cache file locked warning';
2094						$en = -32001;
2095						// do not call error handling for just a warning
2096					}
2097
2098					if ($this->debug) {
2099						ADOConnection::outp( " ".$em);
2100					}
2101				}
2102				if ($rs->EOF && !$eof) {
2103					$rs->MoveFirst();
2104					//$rs = csv2rs($md5file,$err);
2105					$rs->connection = $this; // Pablo suggestion
2106				}
2107
2108			} else if (!$this->memCache) {
2109				$ADODB_CACHE->flushcache($md5file);
2110			}
2111		} else {
2112			$this->_errorMsg = '';
2113			$this->_errorCode = 0;
2114
2115			if ($this->fnCacheExecute) {
2116				$fn = $this->fnCacheExecute;
2117				$fn($this, $secs2cache, $sql, $inputarr);
2118			}
2119			// ok, set cached object found
2120			$rs->connection = $this; // Pablo suggestion
2121			if ($this->debug){
2122				if ($this->debug == 99) {
2123					adodb_backtrace();
2124				}
2125				$inBrowser = isset($_SERVER['HTTP_USER_AGENT']);
2126				$ttl = $rs->timeCreated + $secs2cache - time();
2127				$s = is_array($sql) ? $sql[0] : $sql;
2128				if ($inBrowser) {
2129					$s = '<i>'.htmlspecialchars($s).'</i>';
2130				}
2131
2132				ADOConnection::outp( " $md5file reloaded, ttl=$ttl [ $s ]");
2133			}
2134		}
2135		return $rs;
2136	}
2137
2138
2139	/*
2140		Similar to PEAR DB's autoExecute(), except that
2141		$mode can be 'INSERT' or 'UPDATE' or DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
2142		If $mode == 'UPDATE', then $where is compulsory as a safety measure.
2143
2144		$forceUpdate means that even if the data has not changed, perform update.
2145	 */
2146	function AutoExecute($table, $fields_values, $mode = 'INSERT', $where = false, $forceUpdate = true, $magicq = false) {
2147		if ($where === false && ($mode == 'UPDATE' || $mode == 2 /* DB_AUTOQUERY_UPDATE */) ) {
2148			$this->outp_throw('AutoExecute: Illegal mode=UPDATE with empty WHERE clause', 'AutoExecute');
2149			return false;
2150		}
2151
2152		$sql = "SELECT * FROM $table";
2153		$rs = $this->SelectLimit($sql, 1);
2154		if (!$rs) {
2155			return false; // table does not exist
2156		}
2157
2158		$rs->tableName = $table;
2159		if ($where !== false) {
2160			$sql .= " WHERE $where";
2161		}
2162		$rs->sql = $sql;
2163
2164		switch($mode) {
2165			case 'UPDATE':
2166			case DB_AUTOQUERY_UPDATE:
2167				$sql = $this->GetUpdateSQL($rs, $fields_values, $forceUpdate, $magicq);
2168				break;
2169			case 'INSERT':
2170			case DB_AUTOQUERY_INSERT:
2171				$sql = $this->GetInsertSQL($rs, $fields_values, $magicq);
2172				break;
2173			default:
2174				$this->outp_throw("AutoExecute: Unknown mode=$mode", 'AutoExecute');
2175				return false;
2176		}
2177		return $sql && $this->Execute($sql);
2178	}
2179
2180
2181	/**
2182	 * Generates an Update Query based on an existing recordset.
2183	 * $arrFields is an associative array of fields with the value
2184	 * that should be assigned.
2185	 *
2186	 * Note: This function should only be used on a recordset
2187	 *	   that is run against a single table and sql should only
2188	 *		 be a simple select stmt with no groupby/orderby/limit
2189	 *
2190	 * "Jonathan Younger" <jyounger@unilab.com>
2191	 */
2192	function GetUpdateSQL(&$rs, $arrFields,$forceUpdate=false,$magicq=false,$force=null) {
2193		global $ADODB_INCLUDED_LIB;
2194
2195		// ********************************************************
2196		// This is here to maintain compatibility
2197		// with older adodb versions. Sets force type to force nulls if $forcenulls is set.
2198		if (!isset($force)) {
2199			global $ADODB_FORCE_TYPE;
2200			$force = $ADODB_FORCE_TYPE;
2201		}
2202		// ********************************************************
2203
2204		if (empty($ADODB_INCLUDED_LIB)) {
2205			include(ADODB_DIR.'/adodb-lib.inc.php');
2206		}
2207		return _adodb_getupdatesql($this,$rs,$arrFields,$forceUpdate,$magicq,$force);
2208	}
2209
2210	/**
2211	 * Generates an Insert Query based on an existing recordset.
2212	 * $arrFields is an associative array of fields with the value
2213	 * that should be assigned.
2214	 *
2215	 * Note: This function should only be used on a recordset
2216	 *       that is run against a single table.
2217	 */
2218	function GetInsertSQL(&$rs, $arrFields,$magicq=false,$force=null) {
2219		global $ADODB_INCLUDED_LIB;
2220		if (!isset($force)) {
2221			global $ADODB_FORCE_TYPE;
2222			$force = $ADODB_FORCE_TYPE;
2223		}
2224		if (empty($ADODB_INCLUDED_LIB)) {
2225			include(ADODB_DIR.'/adodb-lib.inc.php');
2226		}
2227		return _adodb_getinsertsql($this,$rs,$arrFields,$magicq,$force);
2228	}
2229
2230
2231	/**
2232	* Update a blob column, given a where clause. There are more sophisticated
2233	* blob handling functions that we could have implemented, but all require
2234	* a very complex API. Instead we have chosen something that is extremely
2235	* simple to understand and use.
2236	*
2237	* Note: $blobtype supports 'BLOB' and 'CLOB', default is BLOB of course.
2238	*
2239	* Usage to update a $blobvalue which has a primary key blob_id=1 into a
2240	* field blobtable.blobcolumn:
2241	*
2242	*	UpdateBlob('blobtable', 'blobcolumn', $blobvalue, 'blob_id=1');
2243	*
2244	* Insert example:
2245	*
2246	*	$conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
2247	*	$conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
2248	*/
2249	function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') {
2250		return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false;
2251	}
2252
2253	/**
2254	* Usage:
2255	*	UpdateBlob('TABLE', 'COLUMN', '/path/to/file', 'ID=1');
2256	*
2257	*	$blobtype supports 'BLOB' and 'CLOB'
2258	*
2259	*	$conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
2260	*	$conn->UpdateBlob('blobtable','blobcol',$blobpath,'id=1');
2261	*/
2262	function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB') {
2263		$fd = fopen($path,'rb');
2264		if ($fd === false) {
2265			return false;
2266		}
2267		$val = fread($fd,filesize($path));
2268		fclose($fd);
2269		return $this->UpdateBlob($table,$column,$val,$where,$blobtype);
2270	}
2271
2272	function BlobDecode($blob) {
2273		return $blob;
2274	}
2275
2276	function BlobEncode($blob) {
2277		return $blob;
2278	}
2279
2280	function GetCharSet() {
2281		return $this->charSet;
2282	}
2283
2284	function SetCharSet($charset) {
2285		$this->charSet = $charset;
2286		return true;
2287	}
2288
2289	function IfNull( $field, $ifNull ) {
2290		return " CASE WHEN $field is null THEN $ifNull ELSE $field END ";
2291	}
2292
2293	function LogSQL($enable=true) {
2294		include_once(ADODB_DIR.'/adodb-perf.inc.php');
2295
2296		if ($enable) {
2297			$this->fnExecute = 'adodb_log_sql';
2298		} else {
2299			$this->fnExecute = false;
2300		}
2301
2302		$old = $this->_logsql;
2303		$this->_logsql = $enable;
2304		if ($enable && !$old) {
2305			$this->_affected = false;
2306		}
2307		return $old;
2308	}
2309
2310	/**
2311	* Usage:
2312	*	UpdateClob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB');
2313	*
2314	*	$conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)');
2315	*	$conn->UpdateClob('clobtable','clobcol',$clob,'id=1');
2316	*/
2317	function UpdateClob($table,$column,$val,$where) {
2318		return $this->UpdateBlob($table,$column,$val,$where,'CLOB');
2319	}
2320
2321	// not the fastest implementation - quick and dirty - jlim
2322	// for best performance, use the actual $rs->MetaType().
2323	function MetaType($t,$len=-1,$fieldobj=false) {
2324
2325		if (empty($this->_metars)) {
2326			$rsclass = $this->rsPrefix.$this->databaseType;
2327			$this->_metars = new $rsclass(false,$this->fetchMode);
2328			$this->_metars->connection = $this;
2329		}
2330		return $this->_metars->MetaType($t,$len,$fieldobj);
2331	}
2332
2333
2334	/**
2335	*  Change the SQL connection locale to a specified locale.
2336	*  This is used to get the date formats written depending on the client locale.
2337	*/
2338	function SetDateLocale($locale = 'En') {
2339		$this->locale = $locale;
2340		switch (strtoupper($locale))
2341		{
2342			case 'EN':
2343				$this->fmtDate="'Y-m-d'";
2344				$this->fmtTimeStamp = "'Y-m-d H:i:s'";
2345				break;
2346
2347			case 'US':
2348				$this->fmtDate = "'m-d-Y'";
2349				$this->fmtTimeStamp = "'m-d-Y H:i:s'";
2350				break;
2351
2352			case 'PT_BR':
2353			case 'NL':
2354			case 'FR':
2355			case 'RO':
2356			case 'IT':
2357				$this->fmtDate="'d-m-Y'";
2358				$this->fmtTimeStamp = "'d-m-Y H:i:s'";
2359				break;
2360
2361			case 'GE':
2362				$this->fmtDate="'d.m.Y'";
2363				$this->fmtTimeStamp = "'d.m.Y H:i:s'";
2364				break;
2365
2366			default:
2367				$this->fmtDate="'Y-m-d'";
2368				$this->fmtTimeStamp = "'Y-m-d H:i:s'";
2369				break;
2370		}
2371	}
2372
2373	/**
2374	 * GetActiveRecordsClass Performs an 'ALL' query
2375	 *
2376	 * @param mixed $class This string represents the class of the current active record
2377	 * @param mixed $table Table used by the active record object
2378	 * @param mixed $whereOrderBy Where, order, by clauses
2379	 * @param mixed $bindarr
2380	 * @param mixed $primkeyArr
2381	 * @param array $extra Query extras: limit, offset...
2382	 * @param mixed $relations Associative array: table's foreign name, "hasMany", "belongsTo"
2383	 * @access public
2384	 * @return void
2385	 */
2386	function GetActiveRecordsClass(
2387			$class, $table,$whereOrderBy=false,$bindarr=false, $primkeyArr=false,
2388			$extra=array(),
2389			$relations=array())
2390	{
2391		global $_ADODB_ACTIVE_DBS;
2392		## reduce overhead of adodb.inc.php -- moved to adodb-active-record.inc.php
2393		## if adodb-active-recordx is loaded -- should be no issue as they will probably use Find()
2394		if (!isset($_ADODB_ACTIVE_DBS)) {
2395			include_once(ADODB_DIR.'/adodb-active-record.inc.php');
2396		}
2397		return adodb_GetActiveRecordsClass($this, $class, $table, $whereOrderBy, $bindarr, $primkeyArr, $extra, $relations);
2398	}
2399
2400	function GetActiveRecords($table,$where=false,$bindarr=false,$primkeyArr=false) {
2401		$arr = $this->GetActiveRecordsClass('ADODB_Active_Record', $table, $where, $bindarr, $primkeyArr);
2402		return $arr;
2403	}
2404
2405	/**
2406	 * Close Connection
2407	 */
2408	function Close() {
2409		$rez = $this->_close();
2410		$this->_queryID = false;
2411		$this->_connectionID = false;
2412		return $rez;
2413	}
2414
2415	/**
2416	 * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans().
2417	 *
2418	 * @return true if succeeded or false if database does not support transactions
2419	 */
2420	function BeginTrans() {
2421		if ($this->debug) {
2422			ADOConnection::outp("BeginTrans: Transactions not supported for this driver");
2423		}
2424		return false;
2425	}
2426
2427	/* set transaction mode */
2428	function SetTransactionMode( $transaction_mode ) {
2429		$transaction_mode = $this->MetaTransaction($transaction_mode, $this->dataProvider);
2430		$this->_transmode  = $transaction_mode;
2431	}
2432/*
2433http://msdn2.microsoft.com/en-US/ms173763.aspx
2434http://dev.mysql.com/doc/refman/5.0/en/innodb-transaction-isolation.html
2435http://www.postgresql.org/docs/8.1/interactive/sql-set-transaction.html
2436http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_10005.htm
2437*/
2438	function MetaTransaction($mode,$db) {
2439		$mode = strtoupper($mode);
2440		$mode = str_replace('ISOLATION LEVEL ','',$mode);
2441
2442		switch($mode) {
2443
2444		case 'READ UNCOMMITTED':
2445			switch($db) {
2446			case 'oci8':
2447			case 'oracle':
2448				return 'ISOLATION LEVEL READ COMMITTED';
2449			default:
2450				return 'ISOLATION LEVEL READ UNCOMMITTED';
2451			}
2452			break;
2453
2454		case 'READ COMMITTED':
2455				return 'ISOLATION LEVEL READ COMMITTED';
2456			break;
2457
2458		case 'REPEATABLE READ':
2459			switch($db) {
2460			case 'oci8':
2461			case 'oracle':
2462				return 'ISOLATION LEVEL SERIALIZABLE';
2463			default:
2464				return 'ISOLATION LEVEL REPEATABLE READ';
2465			}
2466			break;
2467
2468		case 'SERIALIZABLE':
2469				return 'ISOLATION LEVEL SERIALIZABLE';
2470			break;
2471
2472		default:
2473			return $mode;
2474		}
2475	}
2476
2477	/**
2478	 * If database does not support transactions, always return true as data always commited
2479	 *
2480	 * @param $ok  set to false to rollback transaction, true to commit
2481	 *
2482	 * @return true/false.
2483	 */
2484	function CommitTrans($ok=true) {
2485		return true;
2486	}
2487
2488
2489	/**
2490	 * If database does not support transactions, rollbacks always fail, so return false
2491	 *
2492	 * @return true/false.
2493	 */
2494	function RollbackTrans() {
2495		return false;
2496	}
2497
2498
2499	/**
2500	 * return the databases that the driver can connect to.
2501	 * Some databases will return an empty array.
2502	 *
2503	 * @return an array of database names.
2504	 */
2505	function MetaDatabases() {
2506		global $ADODB_FETCH_MODE;
2507
2508		if ($this->metaDatabasesSQL) {
2509			$save = $ADODB_FETCH_MODE;
2510			$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2511
2512			if ($this->fetchMode !== false) {
2513				$savem = $this->SetFetchMode(false);
2514			}
2515
2516			$arr = $this->GetCol($this->metaDatabasesSQL);
2517			if (isset($savem)) {
2518				$this->SetFetchMode($savem);
2519			}
2520			$ADODB_FETCH_MODE = $save;
2521
2522			return $arr;
2523		}
2524
2525		return false;
2526	}
2527
2528	/**
2529	 * List procedures or functions in an array.
2530	 * @param procedureNamePattern  a procedure name pattern; must match the procedure name as it is stored in the database
2531	 * @param catalog a catalog name; must match the catalog name as it is stored in the database;
2532	 * @param schemaPattern a schema name pattern;
2533	 *
2534	 * @return array of procedures on current database.
2535	 *
2536	 * Array(
2537	 *   [name_of_procedure] => Array(
2538	 *     [type] => PROCEDURE or FUNCTION
2539	 *     [catalog] => Catalog_name
2540	 *     [schema] => Schema_name
2541	 *     [remarks] => explanatory comment on the procedure
2542	 *   )
2543	 * )
2544	 */
2545	function MetaProcedures($procedureNamePattern = null, $catalog  = null, $schemaPattern  = null) {
2546		return false;
2547	}
2548
2549
2550	/**
2551	 * @param ttype can either be 'VIEW' or 'TABLE' or false.
2552	 *		If false, both views and tables are returned.
2553	 *		"VIEW" returns only views
2554	 *		"TABLE" returns only tables
2555	 * @param showSchema returns the schema/user with the table name, eg. USER.TABLE
2556	 * @param mask  is the input mask - only supported by oci8 and postgresql
2557	 *
2558	 * @return  array of tables for current database.
2559	 */
2560	function MetaTables($ttype=false,$showSchema=false,$mask=false) {
2561		global $ADODB_FETCH_MODE;
2562
2563		if ($mask) {
2564			return false;
2565		}
2566		if ($this->metaTablesSQL) {
2567			$save = $ADODB_FETCH_MODE;
2568			$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2569
2570			if ($this->fetchMode !== false) {
2571				$savem = $this->SetFetchMode(false);
2572			}
2573
2574			$rs = $this->Execute($this->metaTablesSQL);
2575			if (isset($savem)) {
2576				$this->SetFetchMode($savem);
2577			}
2578			$ADODB_FETCH_MODE = $save;
2579
2580			if ($rs === false) {
2581				return false;
2582			}
2583			$arr = $rs->GetArray();
2584			$arr2 = array();
2585
2586			if ($hast = ($ttype && isset($arr[0][1]))) {
2587				$showt = strncmp($ttype,'T',1);
2588			}
2589
2590			for ($i=0; $i < sizeof($arr); $i++) {
2591				if ($hast) {
2592					if ($showt == 0) {
2593						if (strncmp($arr[$i][1],'T',1) == 0) {
2594							$arr2[] = trim($arr[$i][0]);
2595						}
2596					} else {
2597						if (strncmp($arr[$i][1],'V',1) == 0) {
2598							$arr2[] = trim($arr[$i][0]);
2599						}
2600					}
2601				} else
2602					$arr2[] = trim($arr[$i][0]);
2603			}
2604			$rs->Close();
2605			return $arr2;
2606		}
2607		return false;
2608	}
2609
2610
2611	function _findschema(&$table,&$schema) {
2612		if (!$schema && ($at = strpos($table,'.')) !== false) {
2613			$schema = substr($table,0,$at);
2614			$table = substr($table,$at+1);
2615		}
2616	}
2617
2618	/**
2619	 * List columns in a database as an array of ADOFieldObjects.
2620	 * See top of file for definition of object.
2621	 *
2622	 * @param $table	table name to query
2623	 * @param $normalize	makes table name case-insensitive (required by some databases)
2624	 * @schema is optional database schema to use - not supported by all databases.
2625	 *
2626	 * @return  array of ADOFieldObjects for current table.
2627	 */
2628	function MetaColumns($table,$normalize=true) {
2629		global $ADODB_FETCH_MODE;
2630
2631		if (!empty($this->metaColumnsSQL)) {
2632			$schema = false;
2633			$this->_findschema($table,$schema);
2634
2635			$save = $ADODB_FETCH_MODE;
2636			$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2637			if ($this->fetchMode !== false) {
2638				$savem = $this->SetFetchMode(false);
2639			}
2640			$rs = $this->Execute(sprintf($this->metaColumnsSQL,($normalize)?strtoupper($table):$table));
2641			if (isset($savem)) {
2642				$this->SetFetchMode($savem);
2643			}
2644			$ADODB_FETCH_MODE = $save;
2645			if ($rs === false || $rs->EOF) {
2646				return false;
2647			}
2648
2649			$retarr = array();
2650			while (!$rs->EOF) { //print_r($rs->fields);
2651				$fld = new ADOFieldObject();
2652				$fld->name = $rs->fields[0];
2653				$fld->type = $rs->fields[1];
2654				if (isset($rs->fields[3]) && $rs->fields[3]) {
2655					if ($rs->fields[3]>0) {
2656						$fld->max_length = $rs->fields[3];
2657					}
2658					$fld->scale = $rs->fields[4];
2659					if ($fld->scale>0) {
2660						$fld->max_length += 1;
2661					}
2662				} else {
2663					$fld->max_length = $rs->fields[2];
2664				}
2665
2666				if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) {
2667					$retarr[] = $fld;
2668				} else {
2669					$retarr[strtoupper($fld->name)] = $fld;
2670				}
2671				$rs->MoveNext();
2672			}
2673			$rs->Close();
2674			return $retarr;
2675		}
2676		return false;
2677	}
2678
2679	/**
2680	 * List indexes on a table as an array.
2681	 * @param table  table name to query
2682	 * @param primary true to only show primary keys. Not actually used for most databases
2683	 *
2684	 * @return array of indexes on current table. Each element represents an index, and is itself an associative array.
2685	 *
2686	 * Array(
2687	 *   [name_of_index] => Array(
2688	 *     [unique] => true or false
2689	 *     [columns] => Array(
2690	 *       [0] => firstname
2691	 *       [1] => lastname
2692	 *     )
2693	 *   )
2694	 * )
2695	 */
2696	function MetaIndexes($table, $primary = false, $owner = false) {
2697		return false;
2698	}
2699
2700	/**
2701	 * List columns names in a table as an array.
2702	 * @param table	table name to query
2703	 *
2704	 * @return  array of column names for current table.
2705	 */
2706	function MetaColumnNames($table, $numIndexes=false,$useattnum=false /* only for postgres */) {
2707		$objarr = $this->MetaColumns($table);
2708		if (!is_array($objarr)) {
2709			return false;
2710		}
2711		$arr = array();
2712		if ($numIndexes) {
2713			$i = 0;
2714			if ($useattnum) {
2715				foreach($objarr as $v)
2716					$arr[$v->attnum] = $v->name;
2717
2718			} else
2719				foreach($objarr as $v) $arr[$i++] = $v->name;
2720		} else
2721			foreach($objarr as $v) $arr[strtoupper($v->name)] = $v->name;
2722
2723		return $arr;
2724	}
2725
2726	/**
2727	 * Different SQL databases used different methods to combine strings together.
2728	 * This function provides a wrapper.
2729	 *
2730	 * param s	variable number of string parameters
2731	 *
2732	 * Usage: $db->Concat($str1,$str2);
2733	 *
2734	 * @return concatenated string
2735	 */
2736	function Concat() {
2737		$arr = func_get_args();
2738		return implode($this->concat_operator, $arr);
2739	}
2740
2741
2742	/**
2743	 * Converts a date "d" to a string that the database can understand.
2744	 *
2745	 * @param d	a date in Unix date time format.
2746	 *
2747	 * @return  date string in database date format
2748	 */
2749	function DBDate($d, $isfld=false) {
2750		if (empty($d) && $d !== 0) {
2751			return 'null';
2752		}
2753		if ($isfld) {
2754			return $d;
2755		}
2756		if (is_object($d)) {
2757			return $d->format($this->fmtDate);
2758		}
2759
2760		if (is_string($d) && !is_numeric($d)) {
2761			if ($d === 'null') {
2762				return $d;
2763			}
2764			if (strncmp($d,"'",1) === 0) {
2765				$d = _adodb_safedateq($d);
2766				return $d;
2767			}
2768			if ($this->isoDates) {
2769				return "'$d'";
2770			}
2771			$d = ADOConnection::UnixDate($d);
2772		}
2773
2774		return adodb_date($this->fmtDate,$d);
2775	}
2776
2777	function BindDate($d) {
2778		$d = $this->DBDate($d);
2779		if (strncmp($d,"'",1)) {
2780			return $d;
2781		}
2782
2783		return substr($d,1,strlen($d)-2);
2784	}
2785
2786	function BindTimeStamp($d) {
2787		$d = $this->DBTimeStamp($d);
2788		if (strncmp($d,"'",1)) {
2789			return $d;
2790		}
2791
2792		return substr($d,1,strlen($d)-2);
2793	}
2794
2795
2796	/**
2797	 * Converts a timestamp "ts" to a string that the database can understand.
2798	 *
2799	 * @param ts	a timestamp in Unix date time format.
2800	 *
2801	 * @return  timestamp string in database timestamp format
2802	 */
2803	function DBTimeStamp($ts,$isfld=false) {
2804		if (empty($ts) && $ts !== 0) {
2805			return 'null';
2806		}
2807		if ($isfld) {
2808			return $ts;
2809		}
2810		if (is_object($ts)) {
2811			return $ts->format($this->fmtTimeStamp);
2812		}
2813
2814		# strlen(14) allows YYYYMMDDHHMMSS format
2815		if (!is_string($ts) || (is_numeric($ts) && strlen($ts)<14)) {
2816			return adodb_date($this->fmtTimeStamp,$ts);
2817		}
2818
2819		if ($ts === 'null') {
2820			return $ts;
2821		}
2822		if ($this->isoDates && strlen($ts) !== 14) {
2823			$ts = _adodb_safedate($ts);
2824			return "'$ts'";
2825		}
2826		$ts = ADOConnection::UnixTimeStamp($ts);
2827		return adodb_date($this->fmtTimeStamp,$ts);
2828	}
2829
2830	/**
2831	 * Also in ADORecordSet.
2832	 * @param $v is a date string in YYYY-MM-DD format
2833	 *
2834	 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2835	 */
2836	static function UnixDate($v) {
2837		if (is_object($v)) {
2838		// odbtp support
2839		//( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
2840			return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
2841		}
2842
2843		if (is_numeric($v) && strlen($v) !== 8) {
2844			return $v;
2845		}
2846		if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|", $v, $rr)) {
2847			return false;
2848		}
2849
2850		if ($rr[1] <= TIMESTAMP_FIRST_YEAR) {
2851			return 0;
2852		}
2853
2854		// h-m-s-MM-DD-YY
2855		return @adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
2856	}
2857
2858
2859	/**
2860	 * Also in ADORecordSet.
2861	 * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
2862	 *
2863	 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2864	 */
2865	static function UnixTimeStamp($v) {
2866		if (is_object($v)) {
2867		// odbtp support
2868		//( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
2869			return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
2870		}
2871
2872		if (!preg_match(
2873			"|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ ,-]*(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|",
2874			($v), $rr)) return false;
2875
2876		if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) {
2877			return 0;
2878		}
2879
2880		// h-m-s-MM-DD-YY
2881		if (!isset($rr[5])) {
2882			return  adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
2883		}
2884		return @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]);
2885	}
2886
2887	/**
2888	 * Also in ADORecordSet.
2889	 *
2890	 * Format database date based on user defined format.
2891	 *
2892	 * @param v		is the character date in YYYY-MM-DD format, returned by database
2893	 * @param fmt	is the format to apply to it, using date()
2894	 *
2895	 * @return a date formated as user desires
2896	 */
2897	function UserDate($v,$fmt='Y-m-d',$gmt=false) {
2898		$tt = $this->UnixDate($v);
2899
2900		// $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2901		if (($tt === false || $tt == -1) && $v != false) {
2902			return $v;
2903		} else if ($tt == 0) {
2904			return $this->emptyDate;
2905		} else if ($tt == -1) {
2906			// pre-TIMESTAMP_FIRST_YEAR
2907		}
2908
2909		return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
2910
2911	}
2912
2913	/**
2914	 *
2915	 * @param v		is the character timestamp in YYYY-MM-DD hh:mm:ss format
2916	 * @param fmt	is the format to apply to it, using date()
2917	 *
2918	 * @return a timestamp formated as user desires
2919	 */
2920	function UserTimeStamp($v,$fmt='Y-m-d H:i:s',$gmt=false) {
2921		if (!isset($v)) {
2922			return $this->emptyTimeStamp;
2923		}
2924		# strlen(14) allows YYYYMMDDHHMMSS format
2925		if (is_numeric($v) && strlen($v)<14) {
2926			return ($gmt) ? adodb_gmdate($fmt,$v) : adodb_date($fmt,$v);
2927		}
2928		$tt = $this->UnixTimeStamp($v);
2929		// $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2930		if (($tt === false || $tt == -1) && $v != false) {
2931			return $v;
2932		}
2933		if ($tt == 0) {
2934			return $this->emptyTimeStamp;
2935		}
2936		return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
2937	}
2938
2939	function escape($s,$magic_quotes=false) {
2940		return $this->addq($s,$magic_quotes);
2941	}
2942
2943	/**
2944	* Quotes a string, without prefixing nor appending quotes.
2945	*/
2946	function addq($s,$magic_quotes=false) {
2947		if (!$magic_quotes) {
2948			if ($this->replaceQuote[0] == '\\') {
2949				// only since php 4.0.5
2950				$s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
2951				//$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
2952			}
2953			return  str_replace("'",$this->replaceQuote,$s);
2954		}
2955
2956		// undo magic quotes for "
2957		$s = str_replace('\\"','"',$s);
2958
2959		if ($this->replaceQuote == "\\'" || ini_get('magic_quotes_sybase')) {
2960			// ' already quoted, no need to change anything
2961			return $s;
2962		} else {
2963			// change \' to '' for sybase/mssql
2964			$s = str_replace('\\\\','\\',$s);
2965			return str_replace("\\'",$this->replaceQuote,$s);
2966		}
2967	}
2968
2969	/**
2970	 * Correctly quotes a string so that all strings are escaped. We prefix and append
2971	 * to the string single-quotes.
2972	 * An example is  $db->qstr("Don't bother",magic_quotes_runtime());
2973	 *
2974	 * @param s			the string to quote
2975	 * @param [magic_quotes]	if $s is GET/POST var, set to get_magic_quotes_gpc().
2976	 *				This undoes the stupidity of magic quotes for GPC.
2977	 *
2978	 * @return  quoted string to be sent back to database
2979	 */
2980	function qstr($s,$magic_quotes=false) {
2981		if (!$magic_quotes) {
2982			if ($this->replaceQuote[0] == '\\'){
2983				// only since php 4.0.5
2984				$s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
2985				//$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
2986			}
2987			return  "'".str_replace("'",$this->replaceQuote,$s)."'";
2988		}
2989
2990		// undo magic quotes for "
2991		$s = str_replace('\\"','"',$s);
2992
2993		if ($this->replaceQuote == "\\'" || ini_get('magic_quotes_sybase')) {
2994			// ' already quoted, no need to change anything
2995			return "'$s'";
2996		} else {
2997			// change \' to '' for sybase/mssql
2998			$s = str_replace('\\\\','\\',$s);
2999			return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
3000		}
3001	}
3002
3003
3004	/**
3005	* Will select the supplied $page number from a recordset, given that it is paginated in pages of
3006	* $nrows rows per page. It also saves two boolean values saying if the given page is the first
3007	* and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
3008	*
3009	* See docs-adodb.htm#ex8 for an example of usage.
3010	*
3011	* @param sql
3012	* @param nrows		is the number of rows per page to get
3013	* @param page		is the page number to get (1-based)
3014	* @param [inputarr]	array of bind variables
3015	* @param [secs2cache]		is a private parameter only used by jlim
3016	* @return		the recordset ($rs->databaseType == 'array')
3017	*
3018	* NOTE: phpLens uses a different algorithm and does not use PageExecute().
3019	*
3020	*/
3021	function PageExecute($sql, $nrows, $page, $inputarr=false, $secs2cache=0) {
3022		global $ADODB_INCLUDED_LIB;
3023		if (empty($ADODB_INCLUDED_LIB)) {
3024			include(ADODB_DIR.'/adodb-lib.inc.php');
3025		}
3026		if ($this->pageExecuteCountRows) {
3027			$rs = _adodb_pageexecute_all_rows($this, $sql, $nrows, $page, $inputarr, $secs2cache);
3028		} else {
3029			$rs = _adodb_pageexecute_no_last_page($this, $sql, $nrows, $page, $inputarr, $secs2cache);
3030		}
3031		return $rs;
3032	}
3033
3034
3035	/**
3036	* Will select the supplied $page number from a recordset, given that it is paginated in pages of
3037	* $nrows rows per page. It also saves two boolean values saying if the given page is the first
3038	* and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
3039	*
3040	* @param secs2cache	seconds to cache data, set to 0 to force query
3041	* @param sql
3042	* @param nrows		is the number of rows per page to get
3043	* @param page		is the page number to get (1-based)
3044	* @param [inputarr]	array of bind variables
3045	* @return		the recordset ($rs->databaseType == 'array')
3046	*/
3047	function CachePageExecute($secs2cache, $sql, $nrows, $page,$inputarr=false) {
3048		/*switch($this->dataProvider) {
3049		case 'postgres':
3050		case 'mysql':
3051			break;
3052		default: $secs2cache = 0; break;
3053		}*/
3054		$rs = $this->PageExecute($sql,$nrows,$page,$inputarr,$secs2cache);
3055		return $rs;
3056	}
3057
3058	/**
3059	 * Get the last error recorded by PHP and clear the message.
3060	 *
3061	 * By clearing the message, it becomes possible to detect whether a new error
3062	 * has occurred, even when it is the same error as before being repeated.
3063	 *
3064	 * @return array|null Array if an error has previously occurred. Null otherwise.
3065	 */
3066	protected function resetLastError() {
3067		$error = error_get_last();
3068
3069		if (is_array($error)) {
3070			$error['message'] = '';
3071		}
3072
3073		return $error;
3074	}
3075
3076	/**
3077	 * Compare a previously stored error message with the last error recorded by PHP
3078	 * to determine whether a new error has occured.
3079	 *
3080	 * @param array|null $old Optional. Previously stored return value of error_get_last().
3081	 *
3082	 * @return string The error message if a new error has occured
3083	 *                or an empty string if no (new) errors have occured..
3084	 */
3085	protected function getChangedErrorMsg($old = null) {
3086		$new = error_get_last();
3087
3088		if (is_null($new)) {
3089			// No error has occured yet at all.
3090			return '';
3091		}
3092
3093		if (is_null($old)) {
3094			// First error recorded.
3095			return $new['message'];
3096		}
3097
3098		$changed = false;
3099		foreach($new as $key => $value) {
3100			if ($new[$key] !== $old[$key]) {
3101				$changed = true;
3102				break;
3103			}
3104		}
3105
3106		if ($changed === true) {
3107			return $new['message'];
3108		}
3109
3110		return '';
3111	}
3112
3113} // end class ADOConnection
3114
3115
3116
3117	//==============================================================================================
3118	// CLASS ADOFetchObj
3119	//==============================================================================================
3120
3121	/**
3122	* Internal placeholder for record objects. Used by ADORecordSet->FetchObj().
3123	*/
3124	class ADOFetchObj {
3125	};
3126
3127	//==============================================================================================
3128	// CLASS ADORecordSet_empty
3129	//==============================================================================================
3130
3131	class ADODB_Iterator_empty implements Iterator {
3132
3133		private $rs;
3134
3135		function __construct($rs) {
3136			$this->rs = $rs;
3137		}
3138
3139		function rewind() {}
3140
3141		function valid() {
3142			return !$this->rs->EOF;
3143		}
3144
3145		function key() {
3146			return false;
3147		}
3148
3149		function current() {
3150			return false;
3151		}
3152
3153		function next() {}
3154
3155		function __call($func, $params) {
3156			return call_user_func_array(array($this->rs, $func), $params);
3157		}
3158
3159		function hasMore() {
3160			return false;
3161		}
3162
3163	}
3164
3165
3166	/**
3167	* Lightweight recordset when there are no records to be returned
3168	*/
3169	class ADORecordSet_empty implements IteratorAggregate
3170	{
3171		var $dataProvider = 'empty';
3172		var $databaseType = false;
3173		var $EOF = true;
3174		var $_numOfRows = 0;
3175		var $fields = false;
3176		var $connection = false;
3177
3178		function RowCount() {
3179			return 0;
3180		}
3181
3182		function RecordCount() {
3183			return 0;
3184		}
3185
3186		function PO_RecordCount() {
3187			return 0;
3188		}
3189
3190		function Close() {
3191			return true;
3192		}
3193
3194		function FetchRow() {
3195			return false;
3196		}
3197
3198		function FieldCount() {
3199			return 0;
3200		}
3201
3202		function Init() {}
3203
3204		function getIterator() {
3205			return new ADODB_Iterator_empty($this);
3206		}
3207
3208		function GetAssoc() {
3209			return array();
3210		}
3211
3212		function GetArray() {
3213			return array();
3214		}
3215
3216		function GetAll() {
3217			return array();
3218		}
3219
3220		function GetArrayLimit() {
3221			return array();
3222		}
3223
3224		function GetRows() {
3225			return array();
3226		}
3227
3228		function GetRowAssoc() {
3229			return array();
3230		}
3231
3232		function MaxRecordCount() {
3233			return 0;
3234		}
3235
3236		function NumRows() {
3237			return 0;
3238		}
3239
3240		function NumCols() {
3241			return 0;
3242		}
3243	}
3244
3245	//==============================================================================================
3246	// DATE AND TIME FUNCTIONS
3247	//==============================================================================================
3248	if (!defined('ADODB_DATE_VERSION')) {
3249		include(ADODB_DIR.'/adodb-time.inc.php');
3250	}
3251
3252	//==============================================================================================
3253	// CLASS ADORecordSet
3254	//==============================================================================================
3255
3256	class ADODB_Iterator implements Iterator {
3257
3258		private $rs;
3259
3260		function __construct($rs) {
3261			$this->rs = $rs;
3262		}
3263
3264		function rewind() {
3265			$this->rs->MoveFirst();
3266		}
3267
3268		function valid() {
3269			return !$this->rs->EOF;
3270		}
3271
3272		function key() {
3273			return $this->rs->_currentRow;
3274		}
3275
3276		function current() {
3277			return $this->rs->fields;
3278		}
3279
3280		function next() {
3281			$this->rs->MoveNext();
3282		}
3283
3284		function __call($func, $params) {
3285			return call_user_func_array(array($this->rs, $func), $params);
3286		}
3287
3288		function hasMore() {
3289			return !$this->rs->EOF;
3290		}
3291
3292	}
3293
3294
3295	/**
3296	 * RecordSet class that represents the dataset returned by the database.
3297	 * To keep memory overhead low, this class holds only the current row in memory.
3298	 * No prefetching of data is done, so the RecordCount() can return -1 ( which
3299	 * means recordcount not known).
3300	 */
3301	class ADORecordSet implements IteratorAggregate {
3302
3303	/**
3304	 * public variables
3305	 */
3306	var $dataProvider = "native";
3307	var $fields = false;	/// holds the current row data
3308	var $blobSize = 100;	/// any varchar/char field this size or greater is treated as a blob
3309							/// in other words, we use a text area for editing.
3310	var $canSeek = false;	/// indicates that seek is supported
3311	var $sql;				/// sql text
3312	var $EOF = false;		/// Indicates that the current record position is after the last record in a Recordset object.
3313
3314	var $emptyTimeStamp = '&nbsp;'; /// what to display when $time==0
3315	var $emptyDate = '&nbsp;'; /// what to display when $time==0
3316	var $debug = false;
3317	var $timeCreated=0;		/// datetime in Unix format rs created -- for cached recordsets
3318
3319	var $bind = false;		/// used by Fields() to hold array - should be private?
3320	var $fetchMode;			/// default fetch mode
3321	var $connection = false; /// the parent connection
3322
3323	/**
3324	 *	private variables
3325	 */
3326	var $_numOfRows = -1;	/** number of rows, or -1 */
3327	var $_numOfFields = -1;	/** number of fields in recordset */
3328	var $_queryID = -1;		/** This variable keeps the result link identifier.	*/
3329	var $_currentRow = -1;	/** This variable keeps the current row in the Recordset.	*/
3330	var $_closed = false;	/** has recordset been closed */
3331	var $_inited = false;	/** Init() should only be called once */
3332	var $_obj;				/** Used by FetchObj */
3333	var $_names;			/** Used by FetchObj */
3334
3335	var $_currentPage = -1;	/** Added by Iván Oliva to implement recordset pagination */
3336	var $_atFirstPage = false;	/** Added by Iván Oliva to implement recordset pagination */
3337	var $_atLastPage = false;	/** Added by Iván Oliva to implement recordset pagination */
3338	var $_lastPageNo = -1;
3339	var $_maxRecordCount = 0;
3340	var $datetime = false;
3341
3342	/**
3343	 * Constructor
3344	 *
3345	 * @param queryID	this is the queryID returned by ADOConnection->_query()
3346	 *
3347	 */
3348	function __construct($queryID) {
3349		$this->_queryID = $queryID;
3350	}
3351
3352	function __destruct() {
3353		$this->Close();
3354	}
3355
3356	function getIterator() {
3357		return new ADODB_Iterator($this);
3358	}
3359
3360	/* this is experimental - i don't really know what to return... */
3361	function __toString() {
3362		include_once(ADODB_DIR.'/toexport.inc.php');
3363		return _adodb_export($this,',',',',false,true);
3364	}
3365
3366	function Init() {
3367		if ($this->_inited) {
3368			return;
3369		}
3370		$this->_inited = true;
3371		if ($this->_queryID) {
3372			@$this->_initrs();
3373		} else {
3374			$this->_numOfRows = 0;
3375			$this->_numOfFields = 0;
3376		}
3377		if ($this->_numOfRows != 0 && $this->_numOfFields && $this->_currentRow == -1) {
3378			$this->_currentRow = 0;
3379			if ($this->EOF = ($this->_fetch() === false)) {
3380				$this->_numOfRows = 0; // _numOfRows could be -1
3381			}
3382		} else {
3383			$this->EOF = true;
3384		}
3385	}
3386
3387
3388	/**
3389	 * Generate a SELECT tag from a recordset, and return the HTML markup.
3390	 *
3391	 * If the recordset has 2 columns, we treat the first one as the text to
3392	 * display to the user, and the second as the return value. Extra columns
3393	 * are discarded.
3394	 *
3395	 * @param string       $name            Name of SELECT tag
3396	 * @param string|array $defstr          The value to highlight. Use an array for multiple highlight values.
3397	 * @param bool|string $blank1stItem     True to create an empty item (default), False not to add one;
3398	 *                                      'string' to set its label and 'value:string' to assign a value to it.
3399	 * @param bool         $multiple        True for multi-select list
3400	 * @param int          $size            Number of rows to show (applies to multi-select box only)
3401	 * @param string       $selectAttr      Additional attributes to defined for SELECT tag,
3402	 *                                      useful for holding javascript onChange='...' handlers, CSS class, etc.
3403	 * @param bool         $compareFirstCol When true (default), $defstr is compared against the value (column 2),
3404	 *                                      while false will compare against the description (column 1).
3405	 *
3406	 * @return string HTML
3407	 */
3408	function getMenu($name, $defstr = '', $blank1stItem = true, $multiple = false,
3409					 $size = 0, $selectAttr = '', $compareFirstCol = true)
3410	{
3411		global $ADODB_INCLUDED_LIB;
3412		if (empty($ADODB_INCLUDED_LIB)) {
3413			include(ADODB_DIR.'/adodb-lib.inc.php');
3414		}
3415		return _adodb_getmenu($this, $name, $defstr, $blank1stItem, $multiple,
3416			$size, $selectAttr, $compareFirstCol);
3417	}
3418
3419	/**
3420	 * Generate a SELECT tag with groups from a recordset, and return the HTML markup.
3421	 *
3422	 * The recordset must have 3 columns and be ordered by the 3rd column. The
3423	 * first column contains the text to display to the user, the second is the
3424	 * return value and the third is the option group. Extra columns are discarded.
3425	 * Default strings are compared with the SECOND column.
3426	 *
3427	 * @param string       $name            Name of SELECT tag
3428	 * @param string|array $defstr          The value to highlight. Use an array for multiple highlight values.
3429	 * @param bool|string $blank1stItem     True to create an empty item (default), False not to add one;
3430	 *                                      'string' to set its label and 'value:string' to assign a value to it.
3431	 * @param bool         $multiple        True for multi-select list
3432	 * @param int          $size            Number of rows to show (applies to multi-select box only)
3433	 * @param string       $selectAttr      Additional attributes to defined for SELECT tag,
3434	 *                                      useful for holding javascript onChange='...' handlers, CSS class, etc.
3435	 * @param bool         $compareFirstCol When true (default), $defstr is compared against the value (column 2),
3436	 *                                      while false will compare against the description (column 1).
3437	 *
3438	 * @return string HTML
3439	 */
3440	function getMenuGrouped($name, $defstr = '', $blank1stItem = true, $multiple = false,
3441							$size = 0, $selectAttr = '', $compareFirstCol = true)
3442	{
3443		global $ADODB_INCLUDED_LIB;
3444		if (empty($ADODB_INCLUDED_LIB)) {
3445			include(ADODB_DIR.'/adodb-lib.inc.php');
3446		}
3447		return _adodb_getmenu_gp($this, $name, $defstr, $blank1stItem, $multiple,
3448			$size, $selectAttr, $compareFirstCol);
3449	}
3450
3451	/**
3452	 * Generate a SELECT tag from a recordset, and return the HTML markup.
3453	 *
3454	 * Same as GetMenu(), except that default strings are compared with the
3455	 * FIRST column (the description).
3456	 *
3457	 * @param string       $name            Name of SELECT tag
3458	 * @param string|array $defstr          The value to highlight. Use an array for multiple highlight values.
3459	 * @param bool|string $blank1stItem     True to create an empty item (default), False not to add one;
3460	 *                                      'string' to set its label and 'value:string' to assign a value to it.
3461	 * @param bool         $multiple        True for multi-select list
3462	 * @param int          $size            Number of rows to show (applies to multi-select box only)
3463	 * @param string       $selectAttr      Additional attributes to defined for SELECT tag,
3464	 *                                      useful for holding javascript onChange='...' handlers, CSS class, etc.
3465	 *
3466	 * @return string HTML
3467	 *
3468	 * @deprecated 5.21.0 Use getMenu() with $compareFirstCol = false instead.
3469	 */
3470	function getMenu2($name, $defstr = '', $blank1stItem = true, $multiple = false,
3471					  $size = 0, $selectAttr = '')
3472	{
3473		return $this->getMenu($name, $defstr, $blank1stItem, $multiple,
3474			$size, $selectAttr,false);
3475	}
3476
3477	/**
3478	 * Generate a SELECT tag with groups from a recordset, and return the HTML markup.
3479	 *
3480	 * Same as GetMenuGrouped(), except that default strings are compared with the
3481	 * FIRST column (the description).
3482	 *
3483	 * @param string       $name            Name of SELECT tag
3484	 * @param string|array $defstr          The value to highlight. Use an array for multiple highlight values.
3485	 * @param bool|string $blank1stItem     True to create an empty item (default), False not to add one;
3486	 *                                      'string' to set its label and 'value:string' to assign a value to it.
3487	 * @param bool         $multiple        True for multi-select list
3488	 * @param int          $size            Number of rows to show (applies to multi-select box only)
3489	 * @param string       $selectAttr      Additional attributes to defined for SELECT tag,
3490	 *                                      useful for holding javascript onChange='...' handlers, CSS class, etc.
3491	 *
3492	 * @return string HTML
3493	 *
3494	 * @deprecated 5.21.0 Use getMenuGrouped() with $compareFirstCol = false instead.
3495	 */
3496	function getMenu3($name, $defstr = '', $blank1stItem = true, $multiple = false,
3497					  $size = 0, $selectAttr = '')
3498	{
3499		return $this->getMenuGrouped($name, $defstr, $blank1stItem, $multiple,
3500			$size, $selectAttr, false);
3501	}
3502
3503	/**
3504	 * return recordset as a 2-dimensional array.
3505	 *
3506	 * @param [nRows]  is the number of rows to return. -1 means every row.
3507	 *
3508	 * @return an array indexed by the rows (0-based) from the recordset
3509	 */
3510	function GetArray($nRows = -1) {
3511		global $ADODB_EXTENSION; if ($ADODB_EXTENSION) {
3512		$results = adodb_getall($this,$nRows);
3513		return $results;
3514	}
3515		$results = array();
3516		$cnt = 0;
3517		while (!$this->EOF && $nRows != $cnt) {
3518			$results[] = $this->fields;
3519			$this->MoveNext();
3520			$cnt++;
3521		}
3522		return $results;
3523	}
3524
3525	function GetAll($nRows = -1) {
3526		$arr = $this->GetArray($nRows);
3527		return $arr;
3528	}
3529
3530	/*
3531	* Some databases allow multiple recordsets to be returned. This function
3532	* will return true if there is a next recordset, or false if no more.
3533	*/
3534	function NextRecordSet() {
3535		return false;
3536	}
3537
3538	/**
3539	 * return recordset as a 2-dimensional array.
3540	 * Helper function for ADOConnection->SelectLimit()
3541	 *
3542	 * @param offset	is the row to start calculations from (1-based)
3543	 * @param [nrows]	is the number of rows to return
3544	 *
3545	 * @return an array indexed by the rows (0-based) from the recordset
3546	 */
3547	function GetArrayLimit($nrows,$offset=-1) {
3548		if ($offset <= 0) {
3549			$arr = $this->GetArray($nrows);
3550			return $arr;
3551		}
3552
3553		$this->Move($offset);
3554
3555		$results = array();
3556		$cnt = 0;
3557		while (!$this->EOF && $nrows != $cnt) {
3558			$results[$cnt++] = $this->fields;
3559			$this->MoveNext();
3560		}
3561
3562		return $results;
3563	}
3564
3565
3566	/**
3567	 * Synonym for GetArray() for compatibility with ADO.
3568	 *
3569	 * @param [nRows]  is the number of rows to return. -1 means every row.
3570	 *
3571	 * @return an array indexed by the rows (0-based) from the recordset
3572	 */
3573	function GetRows($nRows = -1) {
3574		$arr = $this->GetArray($nRows);
3575		return $arr;
3576	}
3577
3578	/**
3579	 * return whole recordset as a 2-dimensional associative array if there are more than 2 columns.
3580	 * The first column is treated as the key and is not included in the array.
3581	 * If there is only 2 columns, it will return a 1 dimensional array of key-value pairs unless
3582	 * $force_array == true.
3583	 *
3584	 * @param [force_array] has only meaning if we have 2 data columns. If false, a 1 dimensional
3585	 * array is returned, otherwise a 2 dimensional array is returned. If this sounds confusing,
3586	 * read the source.
3587	 *
3588	 * @param [first2cols] means if there are more than 2 cols, ignore the remaining cols and
3589	 * instead of returning array[col0] => array(remaining cols), return array[col0] => col1
3590	 *
3591	 * @return an associative array indexed by the first column of the array,
3592	 * or false if the  data has less than 2 cols.
3593	 */
3594	function GetAssoc($force_array = false, $first2cols = false) {
3595		global $ADODB_EXTENSION;
3596
3597		$cols = $this->_numOfFields;
3598		if ($cols < 2) {
3599			return false;
3600		}
3601
3602		// Empty recordset
3603		if (!$this->fields) {
3604			return array();
3605		}
3606
3607		// Determine whether the array is associative or 0-based numeric
3608		$numIndex = array_keys($this->fields) == range(0, count($this->fields) - 1);
3609
3610		$results = array();
3611
3612		if (!$first2cols && ($cols > 2 || $force_array)) {
3613			if ($ADODB_EXTENSION) {
3614				if ($numIndex) {
3615					while (!$this->EOF) {
3616						$results[trim($this->fields[0])] = array_slice($this->fields, 1);
3617						adodb_movenext($this);
3618					}
3619				} else {
3620					while (!$this->EOF) {
3621					// Fix for array_slice re-numbering numeric associative keys
3622						$keys = array_slice(array_keys($this->fields), 1);
3623						$sliced_array = array();
3624
3625						foreach($keys as $key) {
3626							$sliced_array[$key] = $this->fields[$key];
3627						}
3628
3629						$results[trim(reset($this->fields))] = $sliced_array;
3630						adodb_movenext($this);
3631					}
3632				}
3633			} else {
3634				if ($numIndex) {
3635					while (!$this->EOF) {
3636						$results[trim($this->fields[0])] = array_slice($this->fields, 1);
3637						$this->MoveNext();
3638					}
3639				} else {
3640					while (!$this->EOF) {
3641					// Fix for array_slice re-numbering numeric associative keys
3642						$keys = array_slice(array_keys($this->fields), 1);
3643						$sliced_array = array();
3644
3645						foreach($keys as $key) {
3646							$sliced_array[$key] = $this->fields[$key];
3647						}
3648
3649						$results[trim(reset($this->fields))] = $sliced_array;
3650						$this->MoveNext();
3651					}
3652				}
3653			}
3654		} else {
3655			if ($ADODB_EXTENSION) {
3656				// return scalar values
3657				if ($numIndex) {
3658					while (!$this->EOF) {
3659					// some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
3660						$results[trim(($this->fields[0]))] = $this->fields[1];
3661						adodb_movenext($this);
3662					}
3663				} else {
3664					while (!$this->EOF) {
3665					// some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
3666						$v1 = trim(reset($this->fields));
3667						$v2 = ''.next($this->fields);
3668						$results[$v1] = $v2;
3669						adodb_movenext($this);
3670					}
3671				}
3672			} else {
3673				if ($numIndex) {
3674					while (!$this->EOF) {
3675					// some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
3676						$results[trim(($this->fields[0]))] = $this->fields[1];
3677						$this->MoveNext();
3678					}
3679				} else {
3680					while (!$this->EOF) {
3681					// some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
3682						$v1 = trim(reset($this->fields));
3683						$v2 = ''.next($this->fields);
3684						$results[$v1] = $v2;
3685						$this->MoveNext();
3686					}
3687				}
3688			}
3689		}
3690
3691		$ref = $results; # workaround accelerator incompat with PHP 4.4 :(
3692		return $ref;
3693	}
3694
3695
3696	/**
3697	 *
3698	 * @param v		is the character timestamp in YYYY-MM-DD hh:mm:ss format
3699	 * @param fmt	is the format to apply to it, using date()
3700	 *
3701	 * @return a timestamp formated as user desires
3702	 */
3703	function UserTimeStamp($v,$fmt='Y-m-d H:i:s') {
3704		if (is_numeric($v) && strlen($v)<14) {
3705			return adodb_date($fmt,$v);
3706		}
3707		$tt = $this->UnixTimeStamp($v);
3708		// $tt == -1 if pre TIMESTAMP_FIRST_YEAR
3709		if (($tt === false || $tt == -1) && $v != false) {
3710			return $v;
3711		}
3712		if ($tt === 0) {
3713			return $this->emptyTimeStamp;
3714		}
3715		return adodb_date($fmt,$tt);
3716	}
3717
3718
3719	/**
3720	 * @param v		is the character date in YYYY-MM-DD format, returned by database
3721	 * @param fmt	is the format to apply to it, using date()
3722	 *
3723	 * @return a date formated as user desires
3724	 */
3725	function UserDate($v,$fmt='Y-m-d') {
3726		$tt = $this->UnixDate($v);
3727		// $tt == -1 if pre TIMESTAMP_FIRST_YEAR
3728		if (($tt === false || $tt == -1) && $v != false) {
3729			return $v;
3730		} else if ($tt == 0) {
3731			return $this->emptyDate;
3732		} else if ($tt == -1) {
3733			// pre-TIMESTAMP_FIRST_YEAR
3734		}
3735		return adodb_date($fmt,$tt);
3736	}
3737
3738
3739	/**
3740	 * @param $v is a date string in YYYY-MM-DD format
3741	 *
3742	 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
3743	 */
3744	static function UnixDate($v) {
3745		return ADOConnection::UnixDate($v);
3746	}
3747
3748
3749	/**
3750	 * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
3751	 *
3752	 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
3753	 */
3754	static function UnixTimeStamp($v) {
3755		return ADOConnection::UnixTimeStamp($v);
3756	}
3757
3758
3759	/**
3760	* PEAR DB Compat - do not use internally
3761	*/
3762	function Free() {
3763		return $this->Close();
3764	}
3765
3766
3767	/**
3768	* PEAR DB compat, number of rows
3769	*/
3770	function NumRows() {
3771		return $this->_numOfRows;
3772	}
3773
3774
3775	/**
3776	* PEAR DB compat, number of cols
3777	*/
3778	function NumCols() {
3779		return $this->_numOfFields;
3780	}
3781
3782	/**
3783	* Fetch a row, returning false if no more rows.
3784	* This is PEAR DB compat mode.
3785	*
3786	* @return false or array containing the current record
3787	*/
3788	function FetchRow() {
3789		if ($this->EOF) {
3790			return false;
3791		}
3792		$arr = $this->fields;
3793		$this->_currentRow++;
3794		if (!$this->_fetch()) {
3795			$this->EOF = true;
3796		}
3797		return $arr;
3798	}
3799
3800
3801	/**
3802	* Fetch a row, returning PEAR_Error if no more rows.
3803	* This is PEAR DB compat mode.
3804	*
3805	* @return DB_OK or error object
3806	*/
3807	function FetchInto(&$arr) {
3808		if ($this->EOF) {
3809			return (defined('PEAR_ERROR_RETURN')) ? new PEAR_Error('EOF',-1): false;
3810		}
3811		$arr = $this->fields;
3812		$this->MoveNext();
3813		return 1; // DB_OK
3814	}
3815
3816
3817	/**
3818	 * Move to the first row in the recordset. Many databases do NOT support this.
3819	 *
3820	 * @return true or false
3821	 */
3822	function MoveFirst() {
3823		if ($this->_currentRow == 0) {
3824			return true;
3825		}
3826		return $this->Move(0);
3827	}
3828
3829
3830	/**
3831	 * Move to the last row in the recordset.
3832	 *
3833	 * @return true or false
3834	 */
3835	function MoveLast() {
3836		if ($this->_numOfRows >= 0) {
3837			return $this->Move($this->_numOfRows-1);
3838		}
3839		if ($this->EOF) {
3840			return false;
3841		}
3842		while (!$this->EOF) {
3843			$f = $this->fields;
3844			$this->MoveNext();
3845		}
3846		$this->fields = $f;
3847		$this->EOF = false;
3848		return true;
3849	}
3850
3851
3852	/**
3853	 * Move to next record in the recordset.
3854	 *
3855	 * @return true if there still rows available, or false if there are no more rows (EOF).
3856	 */
3857	function MoveNext() {
3858		if (!$this->EOF) {
3859			$this->_currentRow++;
3860			if ($this->_fetch()) {
3861				return true;
3862			}
3863		}
3864		$this->EOF = true;
3865		/* -- tested error handling when scrolling cursor -- seems useless.
3866		$conn = $this->connection;
3867		if ($conn && $conn->raiseErrorFn && ($errno = $conn->ErrorNo())) {
3868			$fn = $conn->raiseErrorFn;
3869			$fn($conn->databaseType,'MOVENEXT',$errno,$conn->ErrorMsg().' ('.$this->sql.')',$conn->host,$conn->database);
3870		}
3871		*/
3872		return false;
3873	}
3874
3875
3876	/**
3877	 * Random access to a specific row in the recordset. Some databases do not support
3878	 * access to previous rows in the databases (no scrolling backwards).
3879	 *
3880	 * @param rowNumber is the row to move to (0-based)
3881	 *
3882	 * @return true if there still rows available, or false if there are no more rows (EOF).
3883	 */
3884	function Move($rowNumber = 0) {
3885		$this->EOF = false;
3886		if ($rowNumber == $this->_currentRow) {
3887			return true;
3888		}
3889		if ($rowNumber >= $this->_numOfRows) {
3890			if ($this->_numOfRows != -1) {
3891				$rowNumber = $this->_numOfRows-2;
3892			}
3893		}
3894
3895		if ($rowNumber < 0) {
3896			$this->EOF = true;
3897			return false;
3898		}
3899
3900		if ($this->canSeek) {
3901			if ($this->_seek($rowNumber)) {
3902				$this->_currentRow = $rowNumber;
3903				if ($this->_fetch()) {
3904					return true;
3905				}
3906			} else {
3907				$this->EOF = true;
3908				return false;
3909			}
3910		} else {
3911			if ($rowNumber < $this->_currentRow) {
3912				return false;
3913			}
3914			global $ADODB_EXTENSION;
3915			if ($ADODB_EXTENSION) {
3916				while (!$this->EOF && $this->_currentRow < $rowNumber) {
3917					adodb_movenext($this);
3918				}
3919			} else {
3920				while (! $this->EOF && $this->_currentRow < $rowNumber) {
3921					$this->_currentRow++;
3922
3923					if (!$this->_fetch()) {
3924						$this->EOF = true;
3925					}
3926				}
3927			}
3928			return !($this->EOF);
3929		}
3930
3931		$this->fields = false;
3932		$this->EOF = true;
3933		return false;
3934	}
3935
3936
3937	/**
3938	 * Get the value of a field in the current row by column name.
3939	 * Will not work if ADODB_FETCH_MODE is set to ADODB_FETCH_NUM.
3940	 *
3941	 * @param colname  is the field to access
3942	 *
3943	 * @return the value of $colname column
3944	 */
3945	function Fields($colname) {
3946		return $this->fields[$colname];
3947	}
3948
3949	/**
3950	 * Defines the function to use for table fields case conversion
3951	 * depending on ADODB_ASSOC_CASE
3952	 * @return string strtolower/strtoupper or false if no conversion needed
3953	 */
3954	protected function AssocCaseConvertFunction($case = ADODB_ASSOC_CASE) {
3955		switch($case) {
3956			case ADODB_ASSOC_CASE_UPPER:
3957				return 'strtoupper';
3958			case ADODB_ASSOC_CASE_LOWER:
3959				return 'strtolower';
3960			case ADODB_ASSOC_CASE_NATIVE:
3961			default:
3962				return false;
3963		}
3964	}
3965
3966	/**
3967	 * Builds the bind array associating keys to recordset fields
3968	 *
3969	 * @param int $upper Case for the array keys, defaults to uppercase
3970	 *                   (see ADODB_ASSOC_CASE_xxx constants)
3971	 */
3972	function GetAssocKeys($upper = ADODB_ASSOC_CASE) {
3973		if ($this->bind) {
3974			return;
3975		}
3976		$this->bind = array();
3977
3978		// Define case conversion function for ASSOC fetch mode
3979		$fn_change_case = $this->AssocCaseConvertFunction($upper);
3980
3981		// Build the bind array
3982		for ($i=0; $i < $this->_numOfFields; $i++) {
3983			$o = $this->FetchField($i);
3984
3985			// Set the array's key
3986			if(is_numeric($o->name)) {
3987				// Just use the field ID
3988				$key = $i;
3989			}
3990			elseif( $fn_change_case ) {
3991				// Convert the key's case
3992				$key = $fn_change_case($o->name);
3993			}
3994			else {
3995				$key = $o->name;
3996			}
3997
3998			$this->bind[$key] = $i;
3999		}
4000	}
4001
4002	/**
4003	 * Use associative array to get fields array for databases that do not support
4004	 * associative arrays. Submitted by Paolo S. Asioli paolo.asioli#libero.it
4005	 *
4006	 * @param int $upper Case for the array keys, defaults to uppercase
4007	 *                   (see ADODB_ASSOC_CASE_xxx constants)
4008	 */
4009	function GetRowAssoc($upper = ADODB_ASSOC_CASE) {
4010		$record = array();
4011		$this->GetAssocKeys($upper);
4012
4013		foreach($this->bind as $k => $v) {
4014			if( array_key_exists( $v, $this->fields ) ) {
4015				$record[$k] = $this->fields[$v];
4016			} elseif( array_key_exists( $k, $this->fields ) ) {
4017				$record[$k] = $this->fields[$k];
4018			} else {
4019				# This should not happen... trigger error ?
4020				$record[$k] = null;
4021			}
4022		}
4023		return $record;
4024	}
4025
4026	/**
4027	 * Clean up recordset
4028	 *
4029	 * @return true or false
4030	 */
4031	function Close() {
4032		// free connection object - this seems to globally free the object
4033		// and not merely the reference, so don't do this...
4034		// $this->connection = false;
4035		if (!$this->_closed) {
4036			$this->_closed = true;
4037			return $this->_close();
4038		} else
4039			return true;
4040	}
4041
4042	/**
4043	 * synonyms RecordCount and RowCount
4044	 *
4045	 * @return the number of rows or -1 if this is not supported
4046	 */
4047	function RecordCount() {
4048		return $this->_numOfRows;
4049	}
4050
4051
4052	/*
4053	* If we are using PageExecute(), this will return the maximum possible rows
4054	* that can be returned when paging a recordset.
4055	*/
4056	function MaxRecordCount() {
4057		return ($this->_maxRecordCount) ? $this->_maxRecordCount : $this->RecordCount();
4058	}
4059
4060	/**
4061	 * synonyms RecordCount and RowCount
4062	 *
4063	 * @return the number of rows or -1 if this is not supported
4064	 */
4065	function RowCount() {
4066		return $this->_numOfRows;
4067	}
4068
4069
4070	 /**
4071	 * Portable RecordCount. Pablo Roca <pabloroca@mvps.org>
4072	 *
4073	 * @return  the number of records from a previous SELECT. All databases support this.
4074	 *
4075	 * But aware possible problems in multiuser environments. For better speed the table
4076	 * must be indexed by the condition. Heavy test this before deploying.
4077	 */
4078	function PO_RecordCount($table="", $condition="") {
4079
4080		$lnumrows = $this->_numOfRows;
4081		// the database doesn't support native recordcount, so we do a workaround
4082		if ($lnumrows == -1 && $this->connection) {
4083			IF ($table) {
4084				if ($condition) {
4085					$condition = " WHERE " . $condition;
4086				}
4087				$resultrows = $this->connection->Execute("SELECT COUNT(*) FROM $table $condition");
4088				if ($resultrows) {
4089					$lnumrows = reset($resultrows->fields);
4090				}
4091			}
4092		}
4093		return $lnumrows;
4094	}
4095
4096
4097	/**
4098	 * @return the current row in the recordset. If at EOF, will return the last row. 0-based.
4099	 */
4100	function CurrentRow() {
4101		return $this->_currentRow;
4102	}
4103
4104	/**
4105	 * synonym for CurrentRow -- for ADO compat
4106	 *
4107	 * @return the current row in the recordset. If at EOF, will return the last row. 0-based.
4108	 */
4109	function AbsolutePosition() {
4110		return $this->_currentRow;
4111	}
4112
4113	/**
4114	 * @return the number of columns in the recordset. Some databases will set this to 0
4115	 * if no records are returned, others will return the number of columns in the query.
4116	 */
4117	function FieldCount() {
4118		return $this->_numOfFields;
4119	}
4120
4121
4122	/**
4123	 * Get the ADOFieldObject of a specific column.
4124	 *
4125	 * @param fieldoffset	is the column position to access(0-based).
4126	 *
4127	 * @return the ADOFieldObject for that column, or false.
4128	 */
4129	function FetchField($fieldoffset = -1) {
4130		// must be defined by child class
4131
4132		return false;
4133	}
4134
4135	/**
4136	 * Get the ADOFieldObjects of all columns in an array.
4137	 *
4138	 */
4139	function FieldTypesArray() {
4140		$arr = array();
4141		for ($i=0, $max=$this->_numOfFields; $i < $max; $i++)
4142			$arr[] = $this->FetchField($i);
4143		return $arr;
4144	}
4145
4146	/**
4147	* Return the fields array of the current row as an object for convenience.
4148	* The default case is lowercase field names.
4149	*
4150	* @return the object with the properties set to the fields of the current row
4151	*/
4152	function FetchObj() {
4153		$o = $this->FetchObject(false);
4154		return $o;
4155	}
4156
4157	/**
4158	* Return the fields array of the current row as an object for convenience.
4159	* The default case is uppercase.
4160	*
4161	* @param $isupper to set the object property names to uppercase
4162	*
4163	* @return the object with the properties set to the fields of the current row
4164	*/
4165	function FetchObject($isupper=true) {
4166		if (empty($this->_obj)) {
4167			$this->_obj = new ADOFetchObj();
4168			$this->_names = array();
4169			for ($i=0; $i <$this->_numOfFields; $i++) {
4170				$f = $this->FetchField($i);
4171				$this->_names[] = $f->name;
4172			}
4173		}
4174		$i = 0;
4175		if (PHP_VERSION >= 5) {
4176			$o = clone($this->_obj);
4177		} else {
4178			$o = $this->_obj;
4179		}
4180
4181		for ($i=0; $i <$this->_numOfFields; $i++) {
4182			$name = $this->_names[$i];
4183			if ($isupper) {
4184				$n = strtoupper($name);
4185			} else {
4186				$n = $name;
4187			}
4188
4189			$o->$n = $this->Fields($name);
4190		}
4191		return $o;
4192	}
4193
4194	/**
4195	* Return the fields array of the current row as an object for convenience.
4196	* The default is lower-case field names.
4197	*
4198	* @return the object with the properties set to the fields of the current row,
4199	*	or false if EOF
4200	*
4201	* Fixed bug reported by tim@orotech.net
4202	*/
4203	function FetchNextObj() {
4204		$o = $this->FetchNextObject(false);
4205		return $o;
4206	}
4207
4208
4209	/**
4210	* Return the fields array of the current row as an object for convenience.
4211	* The default is upper case field names.
4212	*
4213	* @param $isupper to set the object property names to uppercase
4214	*
4215	* @return the object with the properties set to the fields of the current row,
4216	*	or false if EOF
4217	*
4218	* Fixed bug reported by tim@orotech.net
4219	*/
4220	function FetchNextObject($isupper=true) {
4221		$o = false;
4222		if ($this->_numOfRows != 0 && !$this->EOF) {
4223			$o = $this->FetchObject($isupper);
4224			$this->_currentRow++;
4225			if ($this->_fetch()) {
4226				return $o;
4227			}
4228		}
4229		$this->EOF = true;
4230		return $o;
4231	}
4232
4233	/**
4234	 * Get the metatype of the column. This is used for formatting. This is because
4235	 * many databases use different names for the same type, so we transform the original
4236	 * type to our standardised version which uses 1 character codes:
4237	 *
4238	 * @param t  is the type passed in. Normally is ADOFieldObject->type.
4239	 * @param len is the maximum length of that field. This is because we treat character
4240	 *	fields bigger than a certain size as a 'B' (blob).
4241	 * @param fieldobj is the field object returned by the database driver. Can hold
4242	 *	additional info (eg. primary_key for mysql).
4243	 *
4244	 * @return the general type of the data:
4245	 *	C for character < 250 chars
4246	 *	X for teXt (>= 250 chars)
4247	 *	B for Binary
4248	 *	N for numeric or floating point
4249	 *	D for date
4250	 *	T for timestamp
4251	 *	L for logical/Boolean
4252	 *	I for integer
4253	 *	R for autoincrement counter/integer
4254	 *
4255	 *
4256	*/
4257	function MetaType($t,$len=-1,$fieldobj=false) {
4258		if (is_object($t)) {
4259			$fieldobj = $t;
4260			$t = $fieldobj->type;
4261			$len = $fieldobj->max_length;
4262		}
4263
4264		// changed in 2.32 to hashing instead of switch stmt for speed...
4265		static $typeMap = array(
4266			'VARCHAR' => 'C',
4267			'VARCHAR2' => 'C',
4268			'CHAR' => 'C',
4269			'C' => 'C',
4270			'STRING' => 'C',
4271			'NCHAR' => 'C',
4272			'NVARCHAR' => 'C',
4273			'VARYING' => 'C',
4274			'BPCHAR' => 'C',
4275			'CHARACTER' => 'C',
4276			'INTERVAL' => 'C',  # Postgres
4277			'MACADDR' => 'C', # postgres
4278			'VAR_STRING' => 'C', # mysql
4279			##
4280			'LONGCHAR' => 'X',
4281			'TEXT' => 'X',
4282			'NTEXT' => 'X',
4283			'M' => 'X',
4284			'X' => 'X',
4285			'CLOB' => 'X',
4286			'NCLOB' => 'X',
4287			'LVARCHAR' => 'X',
4288			##
4289			'BLOB' => 'B',
4290			'IMAGE' => 'B',
4291			'BINARY' => 'B',
4292			'VARBINARY' => 'B',
4293			'LONGBINARY' => 'B',
4294			'B' => 'B',
4295			##
4296			'YEAR' => 'D', // mysql
4297			'DATE' => 'D',
4298			'D' => 'D',
4299			##
4300			'UNIQUEIDENTIFIER' => 'C', # MS SQL Server
4301			##
4302			'SMALLDATETIME' => 'T',
4303			'TIME' => 'T',
4304			'TIMESTAMP' => 'T',
4305			'DATETIME' => 'T',
4306			'DATETIME2' => 'T',
4307			'TIMESTAMPTZ' => 'T',
4308			'T' => 'T',
4309			'TIMESTAMP WITHOUT TIME ZONE' => 'T', // postgresql
4310			##
4311			'BOOL' => 'L',
4312			'BOOLEAN' => 'L',
4313			'BIT' => 'L',
4314			'L' => 'L',
4315			##
4316			'COUNTER' => 'R',
4317			'R' => 'R',
4318			'SERIAL' => 'R', // ifx
4319			'INT IDENTITY' => 'R',
4320			##
4321			'INT' => 'I',
4322			'INT2' => 'I',
4323			'INT4' => 'I',
4324			'INT8' => 'I',
4325			'INTEGER' => 'I',
4326			'INTEGER UNSIGNED' => 'I',
4327			'SHORT' => 'I',
4328			'TINYINT' => 'I',
4329			'SMALLINT' => 'I',
4330			'I' => 'I',
4331			##
4332			'LONG' => 'N', // interbase is numeric, oci8 is blob
4333			'BIGINT' => 'N', // this is bigger than PHP 32-bit integers
4334			'DECIMAL' => 'N',
4335			'DEC' => 'N',
4336			'REAL' => 'N',
4337			'DOUBLE' => 'N',
4338			'DOUBLE PRECISION' => 'N',
4339			'SMALLFLOAT' => 'N',
4340			'FLOAT' => 'N',
4341			'NUMBER' => 'N',
4342			'NUM' => 'N',
4343			'NUMERIC' => 'N',
4344			'MONEY' => 'N',
4345
4346			## informix 9.2
4347			'SQLINT' => 'I',
4348			'SQLSERIAL' => 'I',
4349			'SQLSMINT' => 'I',
4350			'SQLSMFLOAT' => 'N',
4351			'SQLFLOAT' => 'N',
4352			'SQLMONEY' => 'N',
4353			'SQLDECIMAL' => 'N',
4354			'SQLDATE' => 'D',
4355			'SQLVCHAR' => 'C',
4356			'SQLCHAR' => 'C',
4357			'SQLDTIME' => 'T',
4358			'SQLINTERVAL' => 'N',
4359			'SQLBYTES' => 'B',
4360			'SQLTEXT' => 'X',
4361			## informix 10
4362			"SQLINT8" => 'I8',
4363			"SQLSERIAL8" => 'I8',
4364			"SQLNCHAR" => 'C',
4365			"SQLNVCHAR" => 'C',
4366			"SQLLVARCHAR" => 'X',
4367			"SQLBOOL" => 'L'
4368		);
4369
4370		$tmap = false;
4371		$t = strtoupper($t);
4372		$tmap = (isset($typeMap[$t])) ? $typeMap[$t] : 'N';
4373		switch ($tmap) {
4374			case 'C':
4375				// is the char field is too long, return as text field...
4376				if ($this->blobSize >= 0) {
4377					if ($len > $this->blobSize) {
4378						return 'X';
4379					}
4380				} else if ($len > 250) {
4381					return 'X';
4382				}
4383				return 'C';
4384
4385			case 'I':
4386				if (!empty($fieldobj->primary_key)) {
4387					return 'R';
4388				}
4389				return 'I';
4390
4391			case false:
4392				return 'N';
4393
4394			case 'B':
4395				if (isset($fieldobj->binary)) {
4396					return ($fieldobj->binary) ? 'B' : 'X';
4397				}
4398				return 'B';
4399
4400			case 'D':
4401				if (!empty($this->connection) && !empty($this->connection->datetime)) {
4402					return 'T';
4403				}
4404				return 'D';
4405
4406			default:
4407				if ($t == 'LONG' && $this->dataProvider == 'oci8') {
4408					return 'B';
4409				}
4410				return $tmap;
4411		}
4412	}
4413
4414	/**
4415	 * Convert case of field names associative array, if needed
4416	 * @return void
4417	 */
4418	protected function _updatefields()
4419	{
4420		if( empty($this->fields)) {
4421			return;
4422		}
4423
4424		// Determine case conversion function
4425		$fn_change_case = $this->AssocCaseConvertFunction();
4426		if(!$fn_change_case) {
4427			// No conversion needed
4428			return;
4429		}
4430
4431		$arr = array();
4432
4433		// Change the case
4434		foreach($this->fields as $k => $v) {
4435			if (!is_integer($k)) {
4436				$k = $fn_change_case($k);
4437			}
4438			$arr[$k] = $v;
4439		}
4440		$this->fields = $arr;
4441	}
4442
4443	function _close() {}
4444
4445	/**
4446	 * set/returns the current recordset page when paginating
4447	 */
4448	function AbsolutePage($page=-1) {
4449		if ($page != -1) {
4450			$this->_currentPage = $page;
4451		}
4452		return $this->_currentPage;
4453	}
4454
4455	/**
4456	 * set/returns the status of the atFirstPage flag when paginating
4457	 */
4458	function AtFirstPage($status=false) {
4459		if ($status != false) {
4460			$this->_atFirstPage = $status;
4461		}
4462		return $this->_atFirstPage;
4463	}
4464
4465	function LastPageNo($page = false) {
4466		if ($page != false) {
4467			$this->_lastPageNo = $page;
4468		}
4469		return $this->_lastPageNo;
4470	}
4471
4472	/**
4473	 * set/returns the status of the atLastPage flag when paginating
4474	 */
4475	function AtLastPage($status=false) {
4476		if ($status != false) {
4477			$this->_atLastPage = $status;
4478		}
4479		return $this->_atLastPage;
4480	}
4481
4482} // end class ADORecordSet
4483
4484	//==============================================================================================
4485	// CLASS ADORecordSet_array
4486	//==============================================================================================
4487
4488	/**
4489	 * This class encapsulates the concept of a recordset created in memory
4490	 * as an array. This is useful for the creation of cached recordsets.
4491	 *
4492	 * Note that the constructor is different from the standard ADORecordSet
4493	 */
4494	class ADORecordSet_array extends ADORecordSet
4495	{
4496		var $databaseType = 'array';
4497
4498		var $_array;	// holds the 2-dimensional data array
4499		var $_types;	// the array of types of each column (C B I L M)
4500		var $_colnames;	// names of each column in array
4501		var $_skiprow1;	// skip 1st row because it holds column names
4502		var $_fieldobjects; // holds array of field objects
4503		var $canSeek = true;
4504		var $affectedrows = false;
4505		var $insertid = false;
4506		var $sql = '';
4507		var $compat = false;
4508
4509		/**
4510		 * Constructor
4511		 */
4512		function __construct($fakeid=1) {
4513			global $ADODB_FETCH_MODE,$ADODB_COMPAT_FETCH;
4514
4515			// fetch() on EOF does not delete $this->fields
4516			$this->compat = !empty($ADODB_COMPAT_FETCH);
4517			parent::__construct($fakeid); // fake queryID
4518			$this->fetchMode = $ADODB_FETCH_MODE;
4519		}
4520
4521		function _transpose($addfieldnames=true) {
4522			global $ADODB_INCLUDED_LIB;
4523
4524			if (empty($ADODB_INCLUDED_LIB)) {
4525				include(ADODB_DIR.'/adodb-lib.inc.php');
4526			}
4527			$hdr = true;
4528
4529			$fobjs = $addfieldnames ? $this->_fieldobjects : false;
4530			adodb_transpose($this->_array, $newarr, $hdr, $fobjs);
4531			//adodb_pr($newarr);
4532
4533			$this->_skiprow1 = false;
4534			$this->_array = $newarr;
4535			$this->_colnames = $hdr;
4536
4537			adodb_probetypes($newarr,$this->_types);
4538
4539			$this->_fieldobjects = array();
4540
4541			foreach($hdr as $k => $name) {
4542				$f = new ADOFieldObject();
4543				$f->name = $name;
4544				$f->type = $this->_types[$k];
4545				$f->max_length = -1;
4546				$this->_fieldobjects[] = $f;
4547			}
4548			$this->fields = reset($this->_array);
4549
4550			$this->_initrs();
4551
4552		}
4553
4554		/**
4555		 * Setup the array.
4556		 *
4557		 * @param array		is a 2-dimensional array holding the data.
4558		 *			The first row should hold the column names
4559		 *			unless paramter $colnames is used.
4560		 * @param typearr	holds an array of types. These are the same types
4561		 *			used in MetaTypes (C,B,L,I,N).
4562		 * @param [colnames]	array of column names. If set, then the first row of
4563		 *			$array should not hold the column names.
4564		 */
4565		function InitArray($array,$typearr,$colnames=false) {
4566			$this->_array = $array;
4567			$this->_types = $typearr;
4568			if ($colnames) {
4569				$this->_skiprow1 = false;
4570				$this->_colnames = $colnames;
4571			} else {
4572				$this->_skiprow1 = true;
4573				$this->_colnames = $array[0];
4574			}
4575			$this->Init();
4576		}
4577		/**
4578		 * Setup the Array and datatype file objects
4579		 *
4580		 * @param array		is a 2-dimensional array holding the data.
4581		 *			The first row should hold the column names
4582		 *			unless paramter $colnames is used.
4583		 * @param fieldarr	holds an array of ADOFieldObject's.
4584		 */
4585		function InitArrayFields(&$array,&$fieldarr) {
4586			$this->_array = $array;
4587			$this->_skiprow1= false;
4588			if ($fieldarr) {
4589				$this->_fieldobjects = $fieldarr;
4590			}
4591			$this->Init();
4592		}
4593
4594		function GetArray($nRows=-1) {
4595			if ($nRows == -1 && $this->_currentRow <= 0 && !$this->_skiprow1) {
4596				return $this->_array;
4597			} else {
4598				$arr = ADORecordSet::GetArray($nRows);
4599				return $arr;
4600			}
4601		}
4602
4603		function _initrs() {
4604			$this->_numOfRows =  sizeof($this->_array);
4605			if ($this->_skiprow1) {
4606				$this->_numOfRows -= 1;
4607			}
4608
4609			$this->_numOfFields = (isset($this->_fieldobjects))
4610				? sizeof($this->_fieldobjects)
4611				: sizeof($this->_types);
4612		}
4613
4614		/* Use associative array to get fields array */
4615		function Fields($colname) {
4616			$mode = isset($this->adodbFetchMode) ? $this->adodbFetchMode : $this->fetchMode;
4617
4618			if ($mode & ADODB_FETCH_ASSOC) {
4619				if (!isset($this->fields[$colname]) && !is_null($this->fields[$colname])) {
4620					$colname = strtolower($colname);
4621				}
4622				return $this->fields[$colname];
4623			}
4624			if (!$this->bind) {
4625				$this->bind = array();
4626				for ($i=0; $i < $this->_numOfFields; $i++) {
4627					$o = $this->FetchField($i);
4628					$this->bind[strtoupper($o->name)] = $i;
4629				}
4630			}
4631			return $this->fields[$this->bind[strtoupper($colname)]];
4632		}
4633
4634		function FetchField($fieldOffset = -1) {
4635			if (isset($this->_fieldobjects)) {
4636				return $this->_fieldobjects[$fieldOffset];
4637			}
4638			$o =  new ADOFieldObject();
4639			$o->name = $this->_colnames[$fieldOffset];
4640			$o->type =  $this->_types[$fieldOffset];
4641			$o->max_length = -1; // length not known
4642
4643			return $o;
4644		}
4645
4646		function _seek($row) {
4647			if (sizeof($this->_array) && 0 <= $row && $row < $this->_numOfRows) {
4648				$this->_currentRow = $row;
4649				if ($this->_skiprow1) {
4650					$row += 1;
4651				}
4652				$this->fields = $this->_array[$row];
4653				return true;
4654			}
4655			return false;
4656		}
4657
4658		function MoveNext() {
4659			if (!$this->EOF) {
4660				$this->_currentRow++;
4661
4662				$pos = $this->_currentRow;
4663
4664				if ($this->_numOfRows <= $pos) {
4665					if (!$this->compat) {
4666						$this->fields = false;
4667					}
4668				} else {
4669					if ($this->_skiprow1) {
4670						$pos += 1;
4671					}
4672					$this->fields = $this->_array[$pos];
4673					return true;
4674				}
4675				$this->EOF = true;
4676			}
4677
4678			return false;
4679		}
4680
4681		function _fetch() {
4682			$pos = $this->_currentRow;
4683
4684			if ($this->_numOfRows <= $pos) {
4685				if (!$this->compat) {
4686					$this->fields = false;
4687				}
4688				return false;
4689			}
4690			if ($this->_skiprow1) {
4691				$pos += 1;
4692			}
4693			$this->fields = $this->_array[$pos];
4694			return true;
4695		}
4696
4697		function _close() {
4698			return true;
4699		}
4700
4701	} // ADORecordSet_array
4702
4703	//==============================================================================================
4704	// HELPER FUNCTIONS
4705	//==============================================================================================
4706
4707	/**
4708	 * Synonym for ADOLoadCode. Private function. Do not use.
4709	 *
4710	 * @deprecated
4711	 */
4712	function ADOLoadDB($dbType) {
4713		return ADOLoadCode($dbType);
4714	}
4715
4716	/**
4717	 * Load the code for a specific database driver. Private function. Do not use.
4718	 */
4719	function ADOLoadCode($dbType) {
4720		global $ADODB_LASTDB;
4721
4722		if (!$dbType) {
4723			return false;
4724		}
4725		$db = strtolower($dbType);
4726		switch ($db) {
4727			case 'ado':
4728				if (PHP_VERSION >= 5) {
4729					$db = 'ado5';
4730				}
4731				$class = 'ado';
4732				break;
4733
4734			case 'ifx':
4735			case 'maxsql':
4736				$class = $db = 'mysqlt';
4737				break;
4738
4739			case 'pgsql':
4740			case 'postgres':
4741				$class = $db = 'postgres8';
4742				break;
4743
4744			default:
4745				$class = $db; break;
4746		}
4747
4748		$file = "drivers/adodb-$db.inc.php";
4749		@include_once(ADODB_DIR . '/' . $file);
4750		$ADODB_LASTDB = $class;
4751		if (class_exists("ADODB_" . $class)) {
4752			return $class;
4753		}
4754
4755		//ADOConnection::outp(adodb_pr(get_declared_classes(),true));
4756		if (!file_exists($file)) {
4757			ADOConnection::outp("Missing file: $file");
4758		} else {
4759			ADOConnection::outp("Syntax error in file: $file");
4760		}
4761		return false;
4762	}
4763
4764	/**
4765	 * synonym for ADONewConnection for people like me who cannot remember the correct name
4766	 */
4767	function NewADOConnection($db='') {
4768		$tmp = ADONewConnection($db);
4769		return $tmp;
4770	}
4771
4772	/**
4773	 * Instantiate a new Connection class for a specific database driver.
4774	 *
4775	 * @param [db]  is the database Connection object to create. If undefined,
4776	 *	use the last database driver that was loaded by ADOLoadCode().
4777	 *
4778	 * @return the freshly created instance of the Connection class.
4779	 */
4780	function ADONewConnection($db='') {
4781		global $ADODB_NEWCONNECTION, $ADODB_LASTDB;
4782
4783		if (!defined('ADODB_ASSOC_CASE')) {
4784			define('ADODB_ASSOC_CASE', ADODB_ASSOC_CASE_NATIVE);
4785		}
4786		$errorfn = (defined('ADODB_ERROR_HANDLER')) ? ADODB_ERROR_HANDLER : false;
4787		if (($at = strpos($db,'://')) !== FALSE) {
4788			$origdsn = $db;
4789			$fakedsn = 'fake'.substr($origdsn,$at);
4790			if (($at2 = strpos($origdsn,'@/')) !== FALSE) {
4791				// special handling of oracle, which might not have host
4792				$fakedsn = str_replace('@/','@adodb-fakehost/',$fakedsn);
4793			}
4794
4795			if ((strpos($origdsn, 'sqlite')) !== FALSE && stripos($origdsn, '%2F') === FALSE) {
4796				// special handling for SQLite, it only might have the path to the database file.
4797				// If you try to connect to a SQLite database using a dsn
4798				// like 'sqlite:///path/to/database', the 'parse_url' php function
4799				// will throw you an exception with a message such as "unable to parse url"
4800				list($scheme, $path) = explode('://', $origdsn);
4801				$dsna['scheme'] = $scheme;
4802				if ($qmark = strpos($path,'?')) {
4803					$dsn['query'] = substr($path,$qmark+1);
4804					$path = substr($path,0,$qmark);
4805				}
4806				$dsna['path'] = '/' . urlencode($path);
4807			} else
4808				$dsna = @parse_url($fakedsn);
4809
4810			if (!$dsna) {
4811				return false;
4812			}
4813			$dsna['scheme'] = substr($origdsn,0,$at);
4814			if ($at2 !== FALSE) {
4815				$dsna['host'] = '';
4816			}
4817
4818			if (strncmp($origdsn,'pdo',3) == 0) {
4819				$sch = explode('_',$dsna['scheme']);
4820				if (sizeof($sch)>1) {
4821					$dsna['host'] = isset($dsna['host']) ? rawurldecode($dsna['host']) : '';
4822					if ($sch[1] == 'sqlite') {
4823						$dsna['host'] = rawurlencode($sch[1].':'.rawurldecode($dsna['host']));
4824					} else {
4825						$dsna['host'] = rawurlencode($sch[1].':host='.rawurldecode($dsna['host']));
4826					}
4827					$dsna['scheme'] = 'pdo';
4828				}
4829			}
4830
4831			$db = @$dsna['scheme'];
4832			if (!$db) {
4833				return false;
4834			}
4835			$dsna['host'] = isset($dsna['host']) ? rawurldecode($dsna['host']) : '';
4836			$dsna['user'] = isset($dsna['user']) ? rawurldecode($dsna['user']) : '';
4837			$dsna['pass'] = isset($dsna['pass']) ? rawurldecode($dsna['pass']) : '';
4838			$dsna['path'] = isset($dsna['path']) ? rawurldecode(substr($dsna['path'],1)) : ''; # strip off initial /
4839
4840			if (isset($dsna['query'])) {
4841				$opt1 = explode('&',$dsna['query']);
4842				foreach($opt1 as $k => $v) {
4843					$arr = explode('=',$v);
4844					$opt[$arr[0]] = isset($arr[1]) ? rawurldecode($arr[1]) : 1;
4845				}
4846			} else {
4847				$opt = array();
4848			}
4849		}
4850	/*
4851	 *  phptype: Database backend used in PHP (mysql, odbc etc.)
4852	 *  dbsyntax: Database used with regards to SQL syntax etc.
4853	 *  protocol: Communication protocol to use (tcp, unix etc.)
4854	 *  hostspec: Host specification (hostname[:port])
4855	 *  database: Database to use on the DBMS server
4856	 *  username: User name for login
4857	 *  password: Password for login
4858	 */
4859		if (!empty($ADODB_NEWCONNECTION)) {
4860			$obj = $ADODB_NEWCONNECTION($db);
4861
4862		}
4863
4864		if(empty($obj)) {
4865
4866			if (!isset($ADODB_LASTDB)) {
4867				$ADODB_LASTDB = '';
4868			}
4869			if (empty($db)) {
4870				$db = $ADODB_LASTDB;
4871			}
4872			if ($db != $ADODB_LASTDB) {
4873				$db = ADOLoadCode($db);
4874			}
4875
4876			if (!$db) {
4877				if (isset($origdsn)) {
4878					$db = $origdsn;
4879				}
4880				if ($errorfn) {
4881					// raise an error
4882					$ignore = false;
4883					$errorfn('ADONewConnection', 'ADONewConnection', -998,
4884							"could not load the database driver for '$db'",
4885							$db,false,$ignore);
4886				} else {
4887					ADOConnection::outp( "<p>ADONewConnection: Unable to load database driver '$db'</p>",false);
4888				}
4889				return false;
4890			}
4891
4892			$cls = 'ADODB_'.$db;
4893			if (!class_exists($cls)) {
4894				adodb_backtrace();
4895				return false;
4896			}
4897
4898			$obj = new $cls();
4899		}
4900
4901		# constructor should not fail
4902		if ($obj) {
4903			if ($errorfn) {
4904				$obj->raiseErrorFn = $errorfn;
4905			}
4906			if (isset($dsna)) {
4907				if (isset($dsna['port'])) {
4908					$obj->port = $dsna['port'];
4909				}
4910				foreach($opt as $k => $v) {
4911					switch(strtolower($k)) {
4912					case 'new':
4913										$nconnect = true; $persist = true; break;
4914					case 'persist':
4915					case 'persistent':	$persist = $v; break;
4916					case 'debug':		$obj->debug = (integer) $v; break;
4917					#ibase
4918					case 'role':		$obj->role = $v; break;
4919					case 'dialect':	$obj->dialect = (integer) $v; break;
4920					case 'charset':		$obj->charset = $v; $obj->charSet=$v; break;
4921					case 'buffers':		$obj->buffers = $v; break;
4922					case 'fetchmode':   $obj->SetFetchMode($v); break;
4923					#ado
4924					case 'charpage':	$obj->charPage = $v; break;
4925					#mysql, mysqli
4926					case 'clientflags': $obj->clientFlags = $v; break;
4927					#mysql, mysqli, postgres
4928					case 'port': $obj->port = $v; break;
4929					#mysqli
4930					case 'socket': $obj->socket = $v; break;
4931					#oci8
4932					case 'nls_date_format': $obj->NLS_DATE_FORMAT = $v; break;
4933					case 'cachesecs': $obj->cacheSecs = $v; break;
4934					case 'memcache':
4935						$varr = explode(':',$v);
4936						$vlen = sizeof($varr);
4937						if ($vlen == 0) {
4938							break;
4939						}
4940						$obj->memCache = true;
4941						$obj->memCacheHost = explode(',',$varr[0]);
4942						if ($vlen == 1) {
4943							break;
4944						}
4945						$obj->memCachePort = $varr[1];
4946						if ($vlen == 2) {
4947							break;
4948						}
4949						$obj->memCacheCompress = $varr[2] ?  true : false;
4950						break;
4951					}
4952				}
4953				if (empty($persist)) {
4954					$ok = $obj->Connect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
4955				} else if (empty($nconnect)) {
4956					$ok = $obj->PConnect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
4957				} else {
4958					$ok = $obj->NConnect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
4959				}
4960
4961				if (!$ok) {
4962					return false;
4963				}
4964			}
4965		}
4966		return $obj;
4967	}
4968
4969
4970
4971	// $perf == true means called by NewPerfMonitor(), otherwise for data dictionary
4972	function _adodb_getdriver($provider,$drivername,$perf=false) {
4973		switch ($provider) {
4974			case 'odbtp':
4975				if (strncmp('odbtp_',$drivername,6)==0) {
4976					return substr($drivername,6);
4977				}
4978			case 'odbc' :
4979				if (strncmp('odbc_',$drivername,5)==0) {
4980					return substr($drivername,5);
4981				}
4982			case 'ado'  :
4983				if (strncmp('ado_',$drivername,4)==0) {
4984					return substr($drivername,4);
4985				}
4986			case 'native':
4987				break;
4988			default:
4989				return $provider;
4990		}
4991
4992		switch($drivername) {
4993			case 'mysqlt':
4994			case 'mysqli':
4995				$drivername='mysql';
4996				break;
4997			case 'postgres7':
4998			case 'postgres8':
4999				$drivername = 'postgres';
5000				break;
5001			case 'firebird15':
5002				$drivername = 'firebird';
5003				break;
5004			case 'oracle':
5005				$drivername = 'oci8';
5006				break;
5007			case 'access':
5008				if ($perf) {
5009					$drivername = '';
5010				}
5011				break;
5012			case 'db2'   :
5013			case 'sapdb' :
5014				break;
5015			default:
5016				$drivername = 'generic';
5017				break;
5018		}
5019		return $drivername;
5020	}
5021
5022	function NewPerfMonitor(&$conn) {
5023		$drivername = _adodb_getdriver($conn->dataProvider,$conn->databaseType,true);
5024		if (!$drivername || $drivername == 'generic') {
5025			return false;
5026		}
5027		include_once(ADODB_DIR.'/adodb-perf.inc.php');
5028		@include_once(ADODB_DIR."/perf/perf-$drivername.inc.php");
5029		$class = "Perf_$drivername";
5030		if (!class_exists($class)) {
5031			return false;
5032		}
5033		$perf = new $class($conn);
5034
5035		return $perf;
5036	}
5037
5038	function NewDataDictionary(&$conn,$drivername=false) {
5039		if (!$drivername) {
5040			$drivername = _adodb_getdriver($conn->dataProvider,$conn->databaseType);
5041		}
5042
5043		include_once(ADODB_DIR.'/adodb-lib.inc.php');
5044		include_once(ADODB_DIR.'/adodb-datadict.inc.php');
5045		$path = ADODB_DIR."/datadict/datadict-$drivername.inc.php";
5046
5047		if (!file_exists($path)) {
5048			ADOConnection::outp("Dictionary driver '$path' not available");
5049			return false;
5050		}
5051		include_once($path);
5052		$class = "ADODB2_$drivername";
5053		$dict = new $class();
5054		$dict->dataProvider = $conn->dataProvider;
5055		$dict->connection = $conn;
5056		$dict->upperName = strtoupper($drivername);
5057		$dict->quote = $conn->nameQuote;
5058		if (!empty($conn->_connectionID)) {
5059			$dict->serverInfo = $conn->ServerInfo();
5060		}
5061
5062		return $dict;
5063	}
5064
5065
5066
5067	/*
5068		Perform a print_r, with pre tags for better formatting.
5069	*/
5070	function adodb_pr($var,$as_string=false) {
5071		if ($as_string) {
5072			ob_start();
5073		}
5074
5075		if (isset($_SERVER['HTTP_USER_AGENT'])) {
5076			echo " <pre>\n";print_r($var);echo "</pre>\n";
5077		} else {
5078			print_r($var);
5079		}
5080
5081		if ($as_string) {
5082			$s = ob_get_contents();
5083			ob_end_clean();
5084			return $s;
5085		}
5086	}
5087
5088	/*
5089		Perform a stack-crawl and pretty print it.
5090
5091		@param printOrArr  Pass in a boolean to indicate print, or an $exception->trace array (assumes that print is true then).
5092		@param levels Number of levels to display
5093	*/
5094	function adodb_backtrace($printOrArr=true,$levels=9999,$ishtml=null) {
5095		global $ADODB_INCLUDED_LIB;
5096		if (empty($ADODB_INCLUDED_LIB)) {
5097			include(ADODB_DIR.'/adodb-lib.inc.php');
5098		}
5099		return _adodb_backtrace($printOrArr,$levels,0,$ishtml);
5100	}
5101
5102}
5103