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