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