Install latest ADODB. Somewhat hacked up and trimmed.... Hopefully will fix PHP...
authorchriskl <chriskl>
Sat, 3 Sep 2005 05:43:58 +0000 (05:43 +0000)
committerchriskl <chriskl>
Sat, 3 Sep 2005 05:43:58 +0000 (05:43 +0000)
48 files changed:
libraries/adodb/adodb-csvlib.inc.php [new file with mode: 0644]
libraries/adodb/adodb-datadict.inc.php [new file with mode: 0644]
libraries/adodb/adodb-error.inc.php [new file with mode: 0755]
libraries/adodb/adodb-errorhandler.inc.php [new file with mode: 0755]
libraries/adodb/adodb-errorpear.inc.php [new file with mode: 0755]
libraries/adodb/adodb-exceptions.inc.php [new file with mode: 0755]
libraries/adodb/adodb-iterator.inc.php [new file with mode: 0755]
libraries/adodb/adodb-lib.inc.php [new file with mode: 0644]
libraries/adodb/adodb-pager.inc.php [new file with mode: 0644]
libraries/adodb/adodb-pear.inc.php [new file with mode: 0755]
libraries/adodb/adodb-perf.inc.php [new file with mode: 0755]
libraries/adodb/adodb-php4.inc.php [new file with mode: 0755]
libraries/adodb/adodb-time.inc.php [new file with mode: 0644]
libraries/adodb/adodb-xmlschema.inc.php [new file with mode: 0755]
libraries/adodb/adodb.inc.php [new file with mode: 0755]
libraries/adodb/drivers/adodb-postgres.inc.php [new file with mode: 0644]
libraries/adodb/drivers/adodb-postgres64.inc.php [new file with mode: 0644]
libraries/adodb/drivers/adodb-postgres7.inc.php [new file with mode: 0644]
libraries/adodb/drivers/adodb-postgres8.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-ar.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-bg.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-bgutf8.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-ca.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-cn.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-cz.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-da.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-de.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-en.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-es.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-esperanto.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-fr.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-hu.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-it.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-nl.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-pl.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-pt-br.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-ro.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-ru1251.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-sv.inc.php [new file with mode: 0755]
libraries/adodb/lang/adodb-uk1251.inc.php [new file with mode: 0755]
libraries/adodb/license.txt [new file with mode: 0755]
libraries/adodb/pivottable.inc.php [new file with mode: 0644]
libraries/adodb/readme.txt [new file with mode: 0755]
libraries/adodb/rsfilter.inc.php [new file with mode: 0644]
libraries/adodb/server.php [new file with mode: 0755]
libraries/adodb/toexport.inc.php [new file with mode: 0644]
libraries/adodb/tohtml.inc.php [new file with mode: 0755]
libraries/adodb/xmlschema.dtd [new file with mode: 0755]

diff --git a/libraries/adodb/adodb-csvlib.inc.php b/libraries/adodb/adodb-csvlib.inc.php
new file mode 100644 (file)
index 0000000..9ca9c1b
--- /dev/null
@@ -0,0 +1,312 @@
+<?php\r
+\r
+// security - hide paths\r
+if (!defined('ADODB_DIR')) die();\r
+\r
+global $ADODB_INCLUDED_CSV;\r
+$ADODB_INCLUDED_CSV = 1;\r
+\r
+/* \r
+\r
+  V4.65 22 July 2005  (c) 2000-2005 John Lim (jlim@natsoft.com.my). All rights reserved.\r
+  Released under both BSD license and Lesser GPL library license. \r
+  Whenever there is any discrepancy between the two licenses, \r
+  the BSD license will take precedence. See License.txt. \r
+  Set tabs to 4 for best viewing.\r
+  \r
+  Latest version is available at http://adodb.sourceforge.net\r
+  \r
+  Library for CSV serialization. This is used by the csv/proxy driver and is the \r
+  CacheExecute() serialization format. \r
+  \r
+  ==== NOTE ====\r
+  Format documented at http://php.weblogs.com/ADODB_CSV\r
+  ==============\r
+*/\r
+\r
+       /**\r
+        * convert a recordset into special format\r
+        *\r
+        * @param rs    the recordset\r
+        *\r
+        * @return      the CSV formated data\r
+        */\r
+       function _rs2serialize(&$rs,$conn=false,$sql='')\r
+       {\r
+               $max = ($rs) ? $rs->FieldCount() : 0;\r
+               \r
+               if ($sql) $sql = urlencode($sql);\r
+               // metadata setup\r
+               \r
+               if ($max <= 0 || $rs->dataProvider == 'empty') { // is insert/update/delete\r
+                       if (is_object($conn)) {\r
+                               $sql .= ','.$conn->Affected_Rows();\r
+                               $sql .= ','.$conn->Insert_ID();\r
+                       } else\r
+                               $sql .= ',,';\r
+                       \r
+                       $text = "====-1,0,$sql\n";\r
+                       return $text;\r
+               }\r
+               $tt = ($rs->timeCreated) ? $rs->timeCreated : time();\r
+               \r
+               ## changed format from ====0 to ====1\r
+               $line = "====1,$tt,$sql\n";\r
+               \r
+               if ($rs->databaseType == 'array') {\r
+                       $rows =& $rs->_array;\r
+               } else {\r
+                       $rows = array();\r
+                       while (!$rs->EOF) {     \r
+                               $rows[] = $rs->fields;\r
+                               $rs->MoveNext();\r
+                       } \r
+               }\r
+               \r
+               for($i=0; $i < $max; $i++) {\r
+                       $o =& $rs->FetchField($i);\r
+                       $flds[] = $o;\r
+               }\r
+       \r
+               $savefetch = isset($rs->adodbFetchMode) ? $rs->adodbFetchMode : $rs->fetchMode;\r
+               $class = $rs->connection->arrayClass;\r
+               $rs2 = new $class();\r
+               $rs2->sql = $rs->sql;\r
+               $rs2->oldProvider = $rs->dataProvider; \r
+               $rs2->InitArrayFields($rows,$flds);\r
+               $rs2->fetchMode = $savefetch;\r
+               return $line.serialize($rs2);\r
+       }\r
+\r
+       \r
+/**\r
+* Open CSV file and convert it into Data. \r
+*\r
+* @param url           file/ftp/http url\r
+* @param err           returns the error message\r
+* @param timeout       dispose if recordset has been alive for $timeout secs\r
+*\r
+* @return              recordset, or false if error occured. If no\r
+*                      error occurred in sql INSERT/UPDATE/DELETE, \r
+*                      empty recordset is returned\r
+*/\r
+       function &csv2rs($url,&$err,$timeout=0, $rsclass='ADORecordSet_array')\r
+       {\r
+               $false = false;\r
+               $err = false;\r
+               $fp = @fopen($url,'rb');\r
+               if (!$fp) {\r
+                       $err = $url.' file/URL not found';\r
+                       return $false;\r
+               }\r
+               @flock($fp, LOCK_SH);\r
+               $arr = array();\r
+               $ttl = 0;\r
+               \r
+               if ($meta = fgetcsv($fp, 32000, ",")) {\r
+                       // check if error message\r
+                       if (strncmp($meta[0],'****',4) === 0) {\r
+                               $err = trim(substr($meta[0],4,1024));\r
+                               fclose($fp);\r
+                               return $false;\r
+                       }\r
+                       // check for meta data\r
+                       // $meta[0] is -1 means return an empty recordset\r
+                       // $meta[1] contains a time \r
+       \r
+                       if (strncmp($meta[0], '====',4) === 0) {\r
+                       \r
+                               if ($meta[0] == "====-1") {\r
+                                       if (sizeof($meta) < 5) {\r
+                                               $err = "Corrupt first line for format -1";\r
+                                               fclose($fp);\r
+                                               return $false;\r
+                                       }\r
+                                       fclose($fp);\r
+                                       \r
+                                       if ($timeout > 0) {\r
+                                               $err = " Illegal Timeout $timeout ";\r
+                                               return $false;\r
+                                       }\r
+                                       \r
+                                       $rs = new $rsclass($val=true);\r
+                                       $rs->fields = array();\r
+                                       $rs->timeCreated = $meta[1];\r
+                                       $rs->EOF = true;\r
+                                       $rs->_numOfFields = 0;\r
+                                       $rs->sql = urldecode($meta[2]);\r
+                                       $rs->affectedrows = (integer)$meta[3];\r
+                                       $rs->insertid = $meta[4];       \r
+                                       return $rs;\r
+                               } \r
+                       # Under high volume loads, we want only 1 thread/process to _write_file\r
+                       # so that we don't have 50 processes queueing to write the same data.\r
+                       # We use probabilistic timeout, ahead of time.\r
+                       #\r
+                       # -4 sec before timeout, give processes 1/32 chance of timing out\r
+                       # -2 sec before timeout, give processes 1/16 chance of timing out\r
+                       # -1 sec after timeout give processes 1/4 chance of timing out\r
+                       # +0 sec after timeout, give processes 100% chance of timing out\r
+                               if (sizeof($meta) > 1) {\r
+                                       if($timeout >0){ \r
+                                               $tdiff = (integer)( $meta[1]+$timeout - time());\r
+                                               if ($tdiff <= 2) {\r
+                                                       switch($tdiff) {\r
+                                                       case 4:\r
+                                                       case 3:\r
+                                                               if ((rand() & 31) == 0) {\r
+                                                                       fclose($fp);\r
+                                                                       $err = "Timeout 3";\r
+                                                                       return $false;\r
+                                                               }\r
+                                                               break;\r
+                                                       case 2: \r
+                                                               if ((rand() & 15) == 0) {\r
+                                                                       fclose($fp);\r
+                                                                       $err = "Timeout 2";\r
+                                                                       return $false;\r
+                                                               }\r
+                                                               break;\r
+                                                       case 1:\r
+                                                               if ((rand() & 3) == 0) {\r
+                                                                       fclose($fp);\r
+                                                                       $err = "Timeout 1";\r
+                                                                       return $false;\r
+                                                               }\r
+                                                               break;\r
+                                                       default: \r
+                                                               fclose($fp);\r
+                                                               $err = "Timeout 0";\r
+                                                               return $false;\r
+                                                       } // switch\r
+                                                       \r
+                                               } // if check flush cache\r
+                                       }// (timeout>0)\r
+                                       $ttl = $meta[1];\r
+                               }\r
+                               //================================================\r
+                               // new cache format - use serialize extensively...\r
+                               if ($meta[0] === '====1') {\r
+                                       // slurp in the data\r
+                                       $MAXSIZE = 128000;\r
+                                       \r
+                                       $text = fread($fp,$MAXSIZE);\r
+                                       if (strlen($text)) {\r
+                                               while ($txt = fread($fp,$MAXSIZE)) {\r
+                                                       $text .= $txt;\r
+                                               }\r
+                                       }\r
+                                       fclose($fp);\r
+                                       $rs = unserialize($text);\r
+                                       if (is_object($rs)) $rs->timeCreated = $ttl;\r
+                                       else {\r
+                                               $err = "Unable to unserialize recordset";\r
+                                               //echo htmlspecialchars($text),' !--END--!<p>';\r
+                                       }\r
+                                       return $rs;\r
+                               }\r
+                               \r
+                               $meta = false;\r
+                               $meta = fgetcsv($fp, 32000, ",");\r
+                               if (!$meta) {\r
+                                       fclose($fp);\r
+                                       $err = "Unexpected EOF 1";\r
+                                       return $false;\r
+                               }\r
+                       }\r
+\r
+                       // Get Column definitions\r
+                       $flds = array();\r
+                       foreach($meta as $o) {\r
+                               $o2 = explode(':',$o);\r
+                               if (sizeof($o2)!=3) {\r
+                                       $arr[] = $meta;\r
+                                       $flds = false;\r
+                                       break;\r
+                               }\r
+                               $fld = new ADOFieldObject();\r
+                               $fld->name = urldecode($o2[0]);\r
+                               $fld->type = $o2[1];\r
+                               $fld->max_length = $o2[2];\r
+                               $flds[] = $fld;\r
+                       }\r
+               } else {\r
+                       fclose($fp);\r
+                       $err = "Recordset had unexpected EOF 2";\r
+                       return $false;\r
+               }\r
+               \r
+               // slurp in the data\r
+               $MAXSIZE = 128000;\r
+               \r
+               $text = '';\r
+               while ($txt = fread($fp,$MAXSIZE)) {\r
+                       $text .= $txt;\r
+               }\r
+                       \r
+               fclose($fp);\r
+               @$arr = unserialize($text);\r
+               //var_dump($arr);\r
+               if (!is_array($arr)) {\r
+                       $err = "Recordset had unexpected EOF (in serialized recordset)";\r
+                       if (get_magic_quotes_runtime()) $err .= ". Magic Quotes Runtime should be disabled!";\r
+                       return $false;\r
+               }\r
+               $rs = new $rsclass();\r
+               $rs->timeCreated = $ttl;\r
+               $rs->InitArrayFields($arr,$flds);\r
+               return $rs;\r
+       }\r
+       \r
+\r
+       /**\r
+       * Save a file $filename and its $contents (normally for caching) with file locking\r
+       */\r
+       function adodb_write_file($filename, $contents,$debug=false)\r
+       { \r
+       # http://www.php.net/bugs.php?id=9203 Bug that flock fails on Windows\r
+       # So to simulate locking, we assume that rename is an atomic operation.\r
+       # First we delete $filename, then we create a $tempfile write to it and \r
+       # rename to the desired $filename. If the rename works, then we successfully \r
+       # modified the file exclusively.\r
+       # What a stupid need - having to simulate locking.\r
+       # Risks:\r
+       # 1. $tempfile name is not unique -- very very low\r
+       # 2. unlink($filename) fails -- ok, rename will fail\r
+       # 3. adodb reads stale file because unlink fails -- ok, $rs timeout occurs\r
+       # 4. another process creates $filename between unlink() and rename() -- ok, rename() fails and  cache updated\r
+               if (strncmp(PHP_OS,'WIN',3) === 0) {\r
+                       // skip the decimal place\r
+                       $mtime = substr(str_replace(' ','_',microtime()),2); \r
+                       // getmypid() actually returns 0 on Win98 - never mind!\r
+                       $tmpname = $filename.uniqid($mtime).getmypid();\r
+                       if (!($fd = @fopen($tmpname,'a'))) return false;\r
+                       $ok = ftruncate($fd,0);                 \r
+                       if (!fwrite($fd,$contents)) $ok = false;\r
+                       fclose($fd);\r
+                       chmod($tmpname,0644);\r
+                       // the tricky moment\r
+                       @unlink($filename);\r
+                       if (!@rename($tmpname,$filename)) {\r
+                               unlink($tmpname);\r
+                               $ok = false;\r
+                       }\r
+                       if (!$ok) {\r
+                               if ($debug) ADOConnection::outp( " Rename $tmpname ".($ok? 'ok' : 'failed'));\r
+                       }\r
+                       return $ok;\r
+               }\r
+               if (!($fd = @fopen($filename, 'a'))) return false;\r
+               if (flock($fd, LOCK_EX) && ftruncate($fd, 0)) {\r
+                       $ok = fwrite( $fd, $contents );\r
+                       fclose($fd);\r
+                       chmod($filename,0644);\r
+               }else {\r
+                       fclose($fd);\r
+                       if ($debug)ADOConnection::outp( " Failed acquiring lock for $filename<br>\n");\r
+                       $ok = false;\r
+               }\r
+       \r
+               return $ok;\r
+       }\r
+?>
\ No newline at end of file
diff --git a/libraries/adodb/adodb-datadict.inc.php b/libraries/adodb/adodb-datadict.inc.php
new file mode 100644 (file)
index 0000000..0c5efcd
--- /dev/null
@@ -0,0 +1,778 @@
+<?php\r
+\r
+/**\r
+  V4.65 22 July 2005  (c) 2000-2005 John Lim (jlim@natsoft.com.my). All rights reserved.\r
+  Released under both BSD license and Lesser GPL library license. \r
+  Whenever there is any discrepancy between the two licenses, \r
+  the BSD license will take precedence.\r
+       \r
+  Set tabs to 4 for best viewing.\r
\r
+       DOCUMENTATION:\r
+       \r
+               See adodb/tests/test-datadict.php for docs and examples.\r
+*/\r
+\r
+/*\r
+       Test script for parser\r
+*/\r
+\r
+// security - hide paths\r
+if (!defined('ADODB_DIR')) die();\r
+\r
+function Lens_ParseTest()\r
+{\r
+$str = "`zcol ACOL` NUMBER(32,2) DEFAULT 'The \"cow\" (and Jim''s dog) jumps over the moon' PRIMARY, INTI INT AUTO DEFAULT 0, zcol2\"afs ds";\r
+print "<p>$str</p>";\r
+$a= Lens_ParseArgs($str);\r
+print "<pre>";\r
+print_r($a);\r
+print "</pre>";\r
+}\r
+\r
+\r
+if (!function_exists('ctype_alnum')) {\r
+       function ctype_alnum($text) {\r
+               return preg_match('/^[a-z0-9]*$/i', $text);\r
+       }\r
+}\r
+\r
+//Lens_ParseTest();\r
+\r
+/**\r
+       Parse arguments, treat "text" (text) and 'text' as quotation marks.\r
+       To escape, use "" or '' or ))\r
+       \r
+       Will read in "abc def" sans quotes, as: abc def\r
+       Same with 'abc def'.\r
+       However if `abc def`, then will read in as `abc def`\r
+       \r
+       @param endstmtchar    Character that indicates end of statement\r
+       @param tokenchars     Include the following characters in tokens apart from A-Z and 0-9 \r
+       @returns 2 dimensional array containing parsed tokens.\r
+*/\r
+function Lens_ParseArgs($args,$endstmtchar=',',$tokenchars='_.-')\r
+{\r
+       $pos = 0;\r
+       $intoken = false;\r
+       $stmtno = 0;\r
+       $endquote = false;\r
+       $tokens = array();\r
+       $tokens[$stmtno] = array();\r
+       $max = strlen($args);\r
+       $quoted = false;\r
+       \r
+       while ($pos < $max) {\r
+               $ch = substr($args,$pos,1);\r
+               switch($ch) {\r
+               case ' ':\r
+               case "\t":\r
+               case "\n":\r
+               case "\r":\r
+                       if (!$quoted) {\r
+                               if ($intoken) {\r
+                                       $intoken = false;\r
+                                       $tokens[$stmtno][] = implode('',$tokarr);\r
+                               }\r
+                               break;\r
+                       }\r
+                       \r
+                       $tokarr[] = $ch;\r
+                       break;\r
+               \r
+               case '`':\r
+                       if ($intoken) $tokarr[] = $ch;\r
+               case '(':\r
+               case ')':       \r
+               case '"':\r
+               case "'":\r
+                       \r
+                       if ($intoken) {\r
+                               if (empty($endquote)) {\r
+                                       $tokens[$stmtno][] = implode('',$tokarr);\r
+                                       if ($ch == '(') $endquote = ')';\r
+                                       else $endquote = $ch;\r
+                                       $quoted = true;\r
+                                       $intoken = true;\r
+                                       $tokarr = array();\r
+                               } else if ($endquote == $ch) {\r
+                                       $ch2 = substr($args,$pos+1,1);\r
+                                       if ($ch2 == $endquote) {\r
+                                               $pos += 1;\r
+                                               $tokarr[] = $ch2;\r
+                                       } else {\r
+                                               $quoted = false;\r
+                                               $intoken = false;\r
+                                               $tokens[$stmtno][] = implode('',$tokarr);\r
+                                               $endquote = '';\r
+                                       }\r
+                               } else\r
+                                       $tokarr[] = $ch;\r
+                                       \r
+                       }else {\r
+                       \r
+                               if ($ch == '(') $endquote = ')';\r
+                               else $endquote = $ch;\r
+                               $quoted = true;\r
+                               $intoken = true;\r
+                               $tokarr = array();\r
+                               if ($ch == '`') $tokarr[] = '`';\r
+                       }\r
+                       break;\r
+                       \r
+               default:\r
+                       \r
+                       if (!$intoken) {\r
+                               if ($ch == $endstmtchar) {\r
+                                       $stmtno += 1;\r
+                                       $tokens[$stmtno] = array();\r
+                                       break;\r
+                               }\r
+                       \r
+                               $intoken = true;\r
+                               $quoted = false;\r
+                               $endquote = false;\r
+                               $tokarr = array();\r
+       \r
+                       }\r
+                       \r
+                       if ($quoted) $tokarr[] = $ch;\r
+                       else if (ctype_alnum($ch) || strpos($tokenchars,$ch) !== false) $tokarr[] = $ch;\r
+                       else {\r
+                               if ($ch == $endstmtchar) {                      \r
+                                       $tokens[$stmtno][] = implode('',$tokarr);\r
+                                       $stmtno += 1;\r
+                                       $tokens[$stmtno] = array();\r
+                                       $intoken = false;\r
+                                       $tokarr = array();\r
+                                       break;\r
+                               }\r
+                               $tokens[$stmtno][] = implode('',$tokarr);\r
+                               $tokens[$stmtno][] = $ch;\r
+                               $intoken = false;\r
+                       }\r
+               }\r
+               $pos += 1;\r
+       }\r
+       if ($intoken) $tokens[$stmtno][] = implode('',$tokarr);\r
+       \r
+       return $tokens;\r
+}\r
+\r
+\r
+class ADODB_DataDict {\r
+       var $connection;\r
+       var $debug = false;\r
+       var $dropTable = 'DROP TABLE %s';\r
+       var $renameTable = 'RENAME TABLE %s TO %s'; \r
+       var $dropIndex = 'DROP INDEX %s';\r
+       var $addCol = ' ADD';\r
+       var $alterCol = ' ALTER COLUMN';\r
+       var $dropCol = ' DROP COLUMN';\r
+       var $renameColumn = 'ALTER TABLE %s RENAME COLUMN %s TO %s';    // table, old-column, new-column, column-definitions (not used by default)\r
+       var $nameRegex = '\w';\r
+       var $nameRegexBrackets = 'a-zA-Z0-9_\(\)';\r
+       var $schema = false;\r
+       var $serverInfo = array();\r
+       var $autoIncrement = false;\r
+       var $dataProvider;\r
+       var $invalidResizeTypes4 = array('CLOB','BLOB','TEXT','DATE','TIME'); // for changetablesql\r
+       var $blobSize = 100;    /// any varchar/char field this size or greater is treated as a blob\r
+                                                       /// in other words, we use a text area for editting.\r
+       \r
+       function GetCommentSQL($table,$col)\r
+       {\r
+               return false;\r
+       }\r
+       \r
+       function SetCommentSQL($table,$col,$cmt)\r
+       {\r
+               return false;\r
+       }\r
+       \r
+       function MetaTables()\r
+       {\r
+               if (!$this->connection->IsConnected()) return array();\r
+               return $this->connection->MetaTables();\r
+       }\r
+       \r
+       function MetaColumns($tab, $upper=true, $schema=false)\r
+       {\r
+               if (!$this->connection->IsConnected()) return array();\r
+               return $this->connection->MetaColumns($this->TableName($tab), $upper, $schema);\r
+       }\r
+       \r
+       function MetaPrimaryKeys($tab,$owner=false,$intkey=false)\r
+       {\r
+               if (!$this->connection->IsConnected()) return array();\r
+               return $this->connection->MetaPrimaryKeys($this->TableName($tab), $owner, $intkey);\r
+       }\r
+       \r
+       function MetaIndexes($table, $primary = false, $owner = false)\r
+       {\r
+               if (!$this->connection->IsConnected()) return array();\r
+               return $this->connection->MetaIndexes($this->TableName($table), $primary, $owner);\r
+       }\r
+       \r
+       function MetaType($t,$len=-1,$fieldobj=false)\r
+       {\r
+               return ADORecordSet::MetaType($t,$len,$fieldobj);\r
+       }\r
+       \r
+       function NameQuote($name = NULL,$allowBrackets=false)\r
+       {\r
+               if (!is_string($name)) {\r
+                       return FALSE;\r
+               }\r
+               \r
+               $name = trim($name);\r
+               \r
+               if ( !is_object($this->connection) ) {\r
+                       return $name;\r
+               }\r
+               \r
+               $quote = $this->connection->nameQuote;\r
+               \r
+               // if name is of the form `name`, quote it\r
+               if ( preg_match('/^`(.+)`$/', $name, $matches) ) {\r
+                       return $quote . $matches[1] . $quote;\r
+               }\r
+               \r
+               // if name contains special characters, quote it\r
+               $regex = ($allowBrackets) ? $this->nameRegexBrackets : $this->nameRegex;\r
+               \r
+               if ( !preg_match('/^[' . $regex . ']+$/', $name) ) {\r
+                       return $quote . $name . $quote;\r
+               }\r
+               \r
+               return $name;\r
+       }\r
+       \r
+       function TableName($name)\r
+       {\r
+               if ( $this->schema ) {\r
+                       return $this->NameQuote($this->schema) .'.'. $this->NameQuote($name);\r
+               }\r
+               return $this->NameQuote($name);\r
+       }\r
+       \r
+       // Executes the sql array returned by GetTableSQL and GetIndexSQL\r
+       function ExecuteSQLArray($sql, $continueOnError = true)\r
+       {\r
+               $rez = 2;\r
+               $conn = &$this->connection;\r
+               $saved = $conn->debug;\r
+               foreach($sql as $line) {\r
+                       \r
+                       if ($this->debug) $conn->debug = true;\r
+                       $ok = $conn->Execute($line);\r
+                       $conn->debug = $saved;\r
+                       if (!$ok) {\r
+                               if ($this->debug) ADOConnection::outp($conn->ErrorMsg());\r
+                               if (!$continueOnError) return 0;\r
+                               $rez = 1;\r
+                       }\r
+               }\r
+               return $rez;\r
+       }\r
+       \r
+       /*\r
+               Returns the actual type given a character code.\r
+               \r
+               C:  varchar\r
+               X:  CLOB (character large object) or largest varchar size if CLOB is not supported\r
+               C2: Multibyte varchar\r
+               X2: Multibyte CLOB\r
+               \r
+               B:  BLOB (binary large object)\r
+               \r
+               D:  Date\r
+               T:  Date-time \r
+               L:  Integer field suitable for storing booleans (0 or 1)\r
+               I:  Integer\r
+               F:  Floating point number\r
+               N:  Numeric or decimal number\r
+       */\r
+       \r
+       function ActualType($meta)\r
+       {\r
+               return $meta;\r
+       }\r
+       \r
+       function CreateDatabase($dbname,$options=false)\r
+       {\r
+               $options = $this->_Options($options);\r
+               $sql = array();\r
+               \r
+               $s = 'CREATE DATABASE ' . $this->NameQuote($dbname);\r
+               if (isset($options[$this->upperName]))\r
+                       $s .= ' '.$options[$this->upperName];\r
+               \r
+               $sql[] = $s;\r
+               return $sql;\r
+       }\r
+       \r
+       /*\r
+        Generates the SQL to create index. Returns an array of sql strings.\r
+       */\r
+       function CreateIndexSQL($idxname, $tabname, $flds, $idxoptions = false)\r
+       {\r
+               if (!is_array($flds)) {\r
+                       $flds = explode(',',$flds);\r
+               }\r
+               \r
+               foreach($flds as $key => $fld) {\r
+                       # some indexes can use partial fields, eg. index first 32 chars of "name" with NAME(32)\r
+                       $flds[$key] = $this->NameQuote($fld,$allowBrackets=true);\r
+               }\r
+               \r
+               return $this->_IndexSQL($this->NameQuote($idxname), $this->TableName($tabname), $flds, $this->_Options($idxoptions));\r
+       }\r
+       \r
+       function DropIndexSQL ($idxname, $tabname = NULL)\r
+       {\r
+               return array(sprintf($this->dropIndex, $this->NameQuote($idxname), $this->TableName($tabname)));\r
+       }\r
+       \r
+       function SetSchema($schema)\r
+       {\r
+               $this->schema = $schema;\r
+       }\r
+       \r
+       function AddColumnSQL($tabname, $flds)\r
+       {\r
+               $tabname = $this->TableName ($tabname);\r
+               $sql = array();\r
+               list($lines,$pkey) = $this->_GenFields($flds);\r
+               $alter = 'ALTER TABLE ' . $tabname . $this->addCol . ' ';\r
+               foreach($lines as $v) {\r
+                       $sql[] = $alter . $v;\r
+               }\r
+               return $sql;\r
+       }\r
+       \r
+       /**\r
+        * Change the definition of one column\r
+        *\r
+        * As some DBM's can't do that on there own, you need to supply the complete defintion of the new table,\r
+        * to allow, recreating the table and copying the content over to the new table\r
+        * @param string $tabname table-name\r
+        * @param string $flds column-name and type for the changed column\r
+        * @param string $tableflds='' complete defintion of the new table, eg. for postgres, default ''\r
+        * @param array/string $tableoptions='' options for the new table see CreateTableSQL, default ''\r
+        * @return array with SQL strings\r
+        */\r
+       function AlterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='')\r
+       {\r
+               $tabname = $this->TableName ($tabname);\r
+               $sql = array();\r
+               list($lines,$pkey) = $this->_GenFields($flds);\r
+               $alter = 'ALTER TABLE ' . $tabname . $this->alterCol . ' ';\r
+               foreach($lines as $v) {\r
+                       $sql[] = $alter . $v;\r
+               }\r
+               return $sql;\r
+       }\r
+       \r
+       /**\r
+        * Rename one column\r
+        *\r
+        * Some DBM's can only do this together with changeing the type of the column (even if that stays the same, eg. mysql)\r
+        * @param string $tabname table-name\r
+        * @param string $oldcolumn column-name to be renamed\r
+        * @param string $newcolumn new column-name\r
+        * @param string $flds='' complete column-defintion-string like for AddColumnSQL, only used by mysql atm., default=''\r
+        * @return array with SQL strings\r
+        */\r
+       function RenameColumnSQL($tabname,$oldcolumn,$newcolumn,$flds='')\r
+       {\r
+               $tabname = $this->TableName ($tabname);\r
+               if ($flds) {\r
+                       list($lines,$pkey) = $this->_GenFields($flds);\r
+                       list(,$first) = each($lines);\r
+                       list(,$column_def) = split("[\t ]+",$first,2);\r
+               }\r
+               return array(sprintf($this->renameColumn,$tabname,$this->NameQuote($oldcolumn),$this->NameQuote($newcolumn),$column_def));\r
+       }\r
+               \r
+       /**\r
+        * Drop one column\r
+        *\r
+        * Some DBM's can't do that on there own, you need to supply the complete defintion of the new table,\r
+        * to allow, recreating the table and copying the content over to the new table\r
+        * @param string $tabname table-name\r
+        * @param string $flds column-name and type for the changed column\r
+        * @param string $tableflds='' complete defintion of the new table, eg. for postgres, default ''\r
+        * @param array/string $tableoptions='' options for the new table see CreateTableSQL, default ''\r
+        * @return array with SQL strings\r
+        */\r
+       function DropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='')\r
+       {\r
+               $tabname = $this->TableName ($tabname);\r
+               if (!is_array($flds)) $flds = explode(',',$flds);\r
+               $sql = array();\r
+               $alter = 'ALTER TABLE ' . $tabname . $this->dropCol . ' ';\r
+               foreach($flds as $v) {\r
+                       $sql[] = $alter . $this->NameQuote($v);\r
+               }\r
+               return $sql;\r
+       }\r
+       \r
+       function DropTableSQL($tabname)\r
+       {\r
+               return array (sprintf($this->dropTable, $this->TableName($tabname)));\r
+       }\r
+       \r
+       function RenameTableSQL($tabname,$newname)\r
+       {\r
+               return array (sprintf($this->renameTable, $this->TableName($tabname),$this->TableName($newname)));\r
+       }       \r
+       \r
+       /*\r
+        Generate the SQL to create table. Returns an array of sql strings.\r
+       */\r
+       function CreateTableSQL($tabname, $flds, $tableoptions=false)\r
+       {\r
+               if (!$tableoptions) $tableoptions = array();\r
+               \r
+               list($lines,$pkey) = $this->_GenFields($flds, true);\r
+               \r
+               $taboptions = $this->_Options($tableoptions);\r
+               $tabname = $this->TableName ($tabname);\r
+               $sql = $this->_TableSQL($tabname,$lines,$pkey,$taboptions);\r
+               \r
+               $tsql = $this->_Triggers($tabname,$taboptions);\r
+               foreach($tsql as $s) $sql[] = $s;\r
+               \r
+               return $sql;\r
+       }\r
+       \r
+       function _GenFields($flds,$widespacing=false)\r
+       {\r
+               if (is_string($flds)) {\r
+                       $padding = '     ';\r
+                       $txt = $flds.$padding;\r
+                       $flds = array();\r
+                       $flds0 = Lens_ParseArgs($txt,',');\r
+                       $hasparam = false;\r
+                       foreach($flds0 as $f0) {\r
+                               $f1 = array();\r
+                               foreach($f0 as $token) {\r
+                                       switch (strtoupper($token)) {\r
+                                       case 'CONSTRAINT':\r
+                                       case 'DEFAULT': \r
+                                               $hasparam = $token;\r
+                                               break;\r
+                                       default:\r
+                                               if ($hasparam) $f1[$hasparam] = $token;\r
+                                               else $f1[] = $token;\r
+                                               $hasparam = false;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               $flds[] = $f1;\r
+                               \r
+                       }\r
+               }\r
+               $this->autoIncrement = false;\r
+               $lines = array();\r
+               $pkey = array();\r
+               foreach($flds as $fld) {\r
+                       $fld = _array_change_key_case($fld);\r
+               \r
+                       $fname = false;\r
+                       $fdefault = false;\r
+                       $fautoinc = false;\r
+                       $ftype = false;\r
+                       $fsize = false;\r
+                       $fprec = false;\r
+                       $fprimary = false;\r
+                       $fnoquote = false;\r
+                       $fdefts = false;\r
+                       $fdefdate = false;\r
+                       $fconstraint = false;\r
+                       $fnotnull = false;\r
+                       $funsigned = false;\r
+                       \r
+                       //-----------------\r
+                       // Parse attributes\r
+                       foreach($fld as $attr => $v) {\r
+                               if ($attr == 2 && is_numeric($v)) $attr = 'SIZE';\r
+                               else if (is_numeric($attr) && $attr > 1 && !is_numeric($v)) $attr = strtoupper($v);\r
+                               \r
+                               switch($attr) {\r
+                               case '0':\r
+                               case 'NAME':    $fname = $v; break;\r
+                               case '1':\r
+                               case 'TYPE':    $ty = $v; $ftype = $this->ActualType(strtoupper($v)); break;\r
+                               \r
+                               case 'SIZE':    \r
+                                                               $dotat = strpos($v,'.'); if ($dotat === false) $dotat = strpos($v,',');\r
+                                                               if ($dotat === false) $fsize = $v;\r
+                                                               else {\r
+                                                                       $fsize = substr($v,0,$dotat);\r
+                                                                       $fprec = substr($v,$dotat+1);\r
+                                                               }\r
+                                                               break;\r
+                               case 'UNSIGNED': $funsigned = true; break;\r
+                               case 'AUTOINCREMENT':\r
+                               case 'AUTO':    $fautoinc = true; $fnotnull = true; break;\r
+                               case 'KEY':\r
+                               case 'PRIMARY': $fprimary = $v; $fnotnull = true; break;\r
+                               case 'DEF':\r
+                               case 'DEFAULT': $fdefault = $v; break;\r
+                               case 'NOTNULL': $fnotnull = $v; break;\r
+                               case 'NOQUOTE': $fnoquote = $v; break;\r
+                               case 'DEFDATE': $fdefdate = $v; break;\r
+                               case 'DEFTIMESTAMP': $fdefts = $v; break;\r
+                               case 'CONSTRAINT': $fconstraint = $v; break;\r
+                               } //switch\r
+                       } // foreach $fld\r
+                       \r
+                       //--------------------\r
+                       // VALIDATE FIELD INFO\r
+                       if (!strlen($fname)) {\r
+                               if ($this->debug) ADOConnection::outp("Undefined NAME");\r
+                               return false;\r
+                       }\r
+                       \r
+                       $fid = strtoupper(preg_replace('/^`(.+)`$/', '$1', $fname));\r
+                       $fname = $this->NameQuote($fname);\r
+                       \r
+                       if (!strlen($ftype)) {\r
+                               if ($this->debug) ADOConnection::outp("Undefined TYPE for field '$fname'");\r
+                               return false;\r
+                       } else {\r
+                               $ftype = strtoupper($ftype);\r
+                       }\r
+                       \r
+                       $ftype = $this->_GetSize($ftype, $ty, $fsize, $fprec);\r
+                       \r
+                       if ($ty == 'X' || $ty == 'X2' || $ty == 'B') $fnotnull = false; // some blob types do not accept nulls\r
+                       \r
+                       if ($fprimary) $pkey[] = $fname;\r
+                       \r
+                       // some databases do not allow blobs to have defaults\r
+                       if ($ty == 'X') $fdefault = false;\r
+                       \r
+                       //--------------------\r
+                       // CONSTRUCT FIELD SQL\r
+                       if ($fdefts) {\r
+                               if (substr($this->connection->databaseType,0,5) == 'mysql') {\r
+                                       $ftype = 'TIMESTAMP';\r
+                               } else {\r
+                                       $fdefault = $this->connection->sysTimeStamp;\r
+                               }\r
+                       } else if ($fdefdate) {\r
+                               if (substr($this->connection->databaseType,0,5) == 'mysql') {\r
+                                       $ftype = 'TIMESTAMP';\r
+                               } else {\r
+                                       $fdefault = $this->connection->sysDate;\r
+                               }\r
+                       } else if ($fdefault !== false && !$fnoquote)\r
+                               if ($ty == 'C' or $ty == 'X' or \r
+                                       ( substr($fdefault,0,1) != "'" && !is_numeric($fdefault)))\r
+                                       if (strlen($fdefault) != 1 && substr($fdefault,0,1) == ' ' && substr($fdefault,strlen($fdefault)-1) == ' ') \r
+                                               $fdefault = trim($fdefault);\r
+                                       else if (strtolower($fdefault) != 'null')\r
+                                               $fdefault = $this->connection->qstr($fdefault);\r
+                       $suffix = $this->_CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned);\r
+                       \r
+                       if ($widespacing) $fname = str_pad($fname,24);\r
+                       $lines[$fid] = $fname.' '.$ftype.$suffix;\r
+                       \r
+                       if ($fautoinc) $this->autoIncrement = true;\r
+               } // foreach $flds\r
+               \r
+               return array($lines,$pkey);\r
+       }\r
+       /*\r
+                GENERATE THE SIZE PART OF THE DATATYPE\r
+                       $ftype is the actual type\r
+                       $ty is the type defined originally in the DDL\r
+       */\r
+       function _GetSize($ftype, $ty, $fsize, $fprec)\r
+       {\r
+               if (strlen($fsize) && $ty != 'X' && $ty != 'B' && strpos($ftype,'(') === false) {\r
+                       $ftype .= "(".$fsize;\r
+                       if (strlen($fprec)) $ftype .= ",".$fprec;\r
+                       $ftype .= ')';\r
+               }\r
+               return $ftype;\r
+       }\r
+       \r
+       \r
+       // return string must begin with space\r
+       function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint)\r
+       {       \r
+               $suffix = '';\r
+               if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault";\r
+               if ($fnotnull) $suffix .= ' NOT NULL';\r
+               if ($fconstraint) $suffix .= ' '.$fconstraint;\r
+               return $suffix;\r
+       }\r
+       \r
+       function _IndexSQL($idxname, $tabname, $flds, $idxoptions)\r
+       {\r
+               $sql = array();\r
+               \r
+               if ( isset($idxoptions['REPLACE']) || isset($idxoptions['DROP']) ) {\r
+                       $sql[] = sprintf ($this->dropIndex, $idxname);\r
+                       if ( isset($idxoptions['DROP']) )\r
+                               return $sql;\r
+               }\r
+               \r
+               if ( empty ($flds) ) {\r
+                       return $sql;\r
+               }\r
+               \r
+               $unique = isset($idxoptions['UNIQUE']) ? ' UNIQUE' : '';\r
+       \r
+               $s = 'CREATE' . $unique . ' INDEX ' . $idxname . ' ON ' . $tabname . ' ';\r
+               \r
+               if ( isset($idxoptions[$this->upperName]) )\r
+                       $s .= $idxoptions[$this->upperName];\r
+               \r
+               if ( is_array($flds) )\r
+                       $flds = implode(', ',$flds);\r
+               $s .= '(' . $flds . ')';\r
+               $sql[] = $s;\r
+               \r
+               return $sql;\r
+       }\r
+       \r
+       function _DropAutoIncrement($tabname)\r
+       {\r
+               return false;\r
+       }\r
+       \r
+       function _TableSQL($tabname,$lines,$pkey,$tableoptions)\r
+       {\r
+               $sql = array();\r
+               \r
+               if (isset($tableoptions['REPLACE']) || isset ($tableoptions['DROP'])) {\r
+                       $sql[] = sprintf($this->dropTable,$tabname);\r
+                       if ($this->autoIncrement) {\r
+                               $sInc = $this->_DropAutoIncrement($tabname);\r
+                               if ($sInc) $sql[] = $sInc;\r
+                       }\r
+                       if ( isset ($tableoptions['DROP']) ) {\r
+                               return $sql;\r
+                       }\r
+               }\r
+               $s = "CREATE TABLE $tabname (\n";\r
+               $s .= implode(",\n", $lines);\r
+               if (sizeof($pkey)>0) {\r
+                       $s .= ",\n                 PRIMARY KEY (";\r
+                       $s .= implode(", ",$pkey).")";\r
+               }\r
+               if (isset($tableoptions['CONSTRAINTS'])) \r
+                       $s .= "\n".$tableoptions['CONSTRAINTS'];\r
+               \r
+               if (isset($tableoptions[$this->upperName.'_CONSTRAINTS'])) \r
+                       $s .= "\n".$tableoptions[$this->upperName.'_CONSTRAINTS'];\r
+               \r
+               $s .= "\n)";\r
+               if (isset($tableoptions[$this->upperName])) $s .= $tableoptions[$this->upperName];\r
+               $sql[] = $s;\r
+               \r
+               return $sql;\r
+       }\r
+       \r
+       /*\r
+               GENERATE TRIGGERS IF NEEDED\r
+               used when table has auto-incrementing field that is emulated using triggers\r
+       */\r
+       function _Triggers($tabname,$taboptions)\r
+       {\r
+               return array();\r
+       }\r
+       \r
+       /*\r
+               Sanitize options, so that array elements with no keys are promoted to keys\r
+       */\r
+       function _Options($opts)\r
+       {\r
+               if (!is_array($opts)) return array();\r
+               $newopts = array();\r
+               foreach($opts as $k => $v) {\r
+                       if (is_numeric($k)) $newopts[strtoupper($v)] = $v;\r
+                       else $newopts[strtoupper($k)] = $v;\r
+               }\r
+               return $newopts;\r
+       }\r
+       \r
+       /*\r
+       "Florian Buzin [ easywe ]" <florian.buzin#easywe.de>\r
+       \r
+       This function changes/adds new fields to your table. You don't\r
+       have to know if the col is new or not. It will check on its own.\r
+       */\r
+       function ChangeTableSQL($tablename, $flds, $tableoptions = false)\r
+       {\r
+       global $ADODB_FETCH_MODE;\r
+       \r
+               $save = $ADODB_FETCH_MODE;\r
+               $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;\r
+               if ($this->connection->fetchMode !== false) $savem = $this->connection->SetFetchMode(false);\r
+               \r
+               // check table exists\r
+               $save_handler = $this->connection->raiseErrorFn;\r
+               $this->connection->raiseErrorFn = '';\r
+               $cols = $this->MetaColumns($tablename);\r
+               $this->connection->raiseErrorFn = $save_handler;\r
+               \r
+               if (isset($savem)) $this->connection->SetFetchMode($savem);\r
+               $ADODB_FETCH_MODE = $save;\r
+               \r
+               if ( empty($cols)) { \r
+                       return $this->CreateTableSQL($tablename, $flds, $tableoptions);\r
+               }\r
+               \r
+               if (is_array($flds)) {\r
+               // Cycle through the update fields, comparing\r
+               // existing fields to fields to update.\r
+               // if the Metatype and size is exactly the\r
+               // same, ignore - by Mark Newham\r
+                       $holdflds = array();\r
+                       foreach($flds as $k=>$v) {\r
+                               if ( isset($cols[$k]) && is_object($cols[$k]) ) {\r
+                                       $c = $cols[$k];\r
+                                       $ml = $c->max_length;\r
+                                       $mt = &$this->MetaType($c->type,$ml);\r
+                                       if ($ml == -1) $ml = '';\r
+                                       if ($mt == 'X') $ml = $v['SIZE'];\r
+                                       if (($mt != $v['TYPE']) ||  $ml != $v['SIZE']) {\r
+                                               $holdflds[$k] = $v;\r
+                                       }\r
+                               } else {\r
+                                       $holdflds[$k] = $v;\r
+                               }               \r
+                       }\r
+                       $flds = $holdflds;\r
+               }\r
+       \r
+\r
+               // already exists, alter table instead\r
+               list($lines,$pkey) = $this->_GenFields($flds);\r
+               $alter = 'ALTER TABLE ' . $this->TableName($tablename);\r
+               $sql = array();\r
+\r
+               foreach ( $lines as $id => $v ) {\r
+                       if ( isset($cols[$id]) && is_object($cols[$id]) ) {\r
+                       \r
+                               $flds = Lens_ParseArgs($v,',');\r
+                               \r
+                               //  We are trying to change the size of the field, if not allowed, simply ignore the request.\r
+                               if ($flds && in_array(strtoupper(substr($flds[0][1],0,4)),$this->invalidResizeTypes4)) continue;         \r
+                       \r
+                               $sql[] = $alter . $this->alterCol . ' ' . $v;\r
+                       } else {\r
+                               $sql[] = $alter . $this->addCol . ' ' . $v;\r
+                       }\r
+               }\r
+               \r
+               return $sql;\r
+       }\r
+} // class\r
+?>
\ No newline at end of file
diff --git a/libraries/adodb/adodb-error.inc.php b/libraries/adodb/adodb-error.inc.php
new file mode 100755 (executable)
index 0000000..8d6e5ae
--- /dev/null
@@ -0,0 +1,258 @@
+<?php\r
+/** \r
+ * @version V4.65 22 July 2005 (c) 2000-2005 John Lim (jlim@natsoft.com.my). All rights reserved.\r
+ * Released under both BSD license and Lesser GPL library license. \r
+ * Whenever there is any discrepancy between the two licenses, \r
+ * the BSD license will take precedence. \r
+ *\r
+ * Set tabs to 4 for best viewing.\r
+ * \r
+ * The following code is adapted from the PEAR DB error handling code.\r
+ * Portions (c)1997-2002 The PHP Group.\r
+ */\r
+\r
+\r
+if (!defined("DB_ERROR")) define("DB_ERROR",-1);\r
+\r
+if (!defined("DB_ERROR_SYNTAX")) {\r
+       define("DB_ERROR_SYNTAX",              -2);\r
+       define("DB_ERROR_CONSTRAINT",          -3);\r
+       define("DB_ERROR_NOT_FOUND",           -4);\r
+       define("DB_ERROR_ALREADY_EXISTS",      -5);\r
+       define("DB_ERROR_UNSUPPORTED",         -6);\r
+       define("DB_ERROR_MISMATCH",            -7);\r
+       define("DB_ERROR_INVALID",             -8);\r
+       define("DB_ERROR_NOT_CAPABLE",         -9);\r
+       define("DB_ERROR_TRUNCATED",          -10);\r
+       define("DB_ERROR_INVALID_NUMBER",     -11);\r
+       define("DB_ERROR_INVALID_DATE",       -12);\r
+       define("DB_ERROR_DIVZERO",            -13);\r
+       define("DB_ERROR_NODBSELECTED",       -14);\r
+       define("DB_ERROR_CANNOT_CREATE",      -15);\r
+       define("DB_ERROR_CANNOT_DELETE",      -16);\r
+       define("DB_ERROR_CANNOT_DROP",        -17);\r
+       define("DB_ERROR_NOSUCHTABLE",        -18);\r
+       define("DB_ERROR_NOSUCHFIELD",        -19);\r
+       define("DB_ERROR_NEED_MORE_DATA",     -20);\r
+       define("DB_ERROR_NOT_LOCKED",         -21);\r
+       define("DB_ERROR_VALUE_COUNT_ON_ROW", -22);\r
+       define("DB_ERROR_INVALID_DSN",        -23);\r
+       define("DB_ERROR_CONNECT_FAILED",     -24);\r
+       define("DB_ERROR_EXTENSION_NOT_FOUND",-25);\r
+       define("DB_ERROR_NOSUCHDB",           -25);\r
+       define("DB_ERROR_ACCESS_VIOLATION",   -26);\r
+}\r
+\r
+function adodb_errormsg($value)\r
+{\r
+global $ADODB_LANG,$ADODB_LANG_ARRAY;\r
+\r
+       if (empty($ADODB_LANG)) $ADODB_LANG = 'en';\r
+       if (isset($ADODB_LANG_ARRAY['LANG']) && $ADODB_LANG_ARRAY['LANG'] == $ADODB_LANG) ;\r
+       else {\r
+               include_once(ADODB_DIR."/lang/adodb-$ADODB_LANG.inc.php");\r
+    }\r
+       return isset($ADODB_LANG_ARRAY[$value]) ? $ADODB_LANG_ARRAY[$value] : $ADODB_LANG_ARRAY[DB_ERROR];\r
+}\r
+\r
+function adodb_error($provider,$dbType,$errno)\r
+{\r
+       //var_dump($errno);\r
+       if (is_numeric($errno) && $errno == 0) return 0;\r
+       switch($provider) { \r
+       case 'mysql': $map = adodb_error_mysql(); break;\r
+       \r
+       case 'oracle':\r
+       case 'oci8': $map = adodb_error_oci8(); break;\r
+       \r
+       case 'ibase': $map = adodb_error_ibase(); break;\r
+       \r
+       case 'odbc': $map = adodb_error_odbc(); break;\r
+       \r
+       case 'mssql':\r
+       case 'sybase': $map = adodb_error_mssql(); break;\r
+       \r
+       case 'informix': $map = adodb_error_ifx(); break;\r
+       \r
+       case 'postgres': return adodb_error_pg($errno); break;\r
+       \r
+       case 'sqlite': return $map = adodb_error_sqlite(); break;\r
+       default:\r
+               return DB_ERROR;\r
+       }       \r
+       //print_r($map);\r
+       //var_dump($errno);\r
+       if (isset($map[$errno])) return $map[$errno];\r
+       return DB_ERROR;\r
+}\r
+\r
+//**************************************************************************************\r
+\r
+function adodb_error_pg($errormsg)\r
+{\r
+       if (is_numeric($errormsg)) return (integer) $errormsg;\r
+    static $error_regexps = array(\r
+            '/(Table does not exist\.|Relation [\"\'].*[\"\'] does not exist|sequence does not exist|class ".+" not found)$/' => DB_ERROR_NOSUCHTABLE,\r
+            '/Relation [\"\'].*[\"\'] already exists|Cannot insert a duplicate key into (a )?unique index.*/'      => DB_ERROR_ALREADY_EXISTS,\r
+            '/divide by zero$/'                     => DB_ERROR_DIVZERO,\r
+            '/pg_atoi: error in .*: can\'t parse /' => DB_ERROR_INVALID_NUMBER,\r
+            '/ttribute [\"\'].*[\"\'] not found|Relation [\"\'].*[\"\'] does not have attribute [\"\'].*[\"\']/' => DB_ERROR_NOSUCHFIELD,\r
+            '/parser: parse error at or near \"/'   => DB_ERROR_SYNTAX,\r
+            '/referential integrity violation/'     => DB_ERROR_CONSTRAINT,\r
+                       '/Relation [\"\'].*[\"\'] already exists|Cannot insert a duplicate key into (a )?unique index.*|duplicate key violates unique constraint/'     \r
+                                => DB_ERROR_ALREADY_EXISTS\r
+        );\r
+       reset($error_regexps);\r
+    while (list($regexp,$code) = each($error_regexps)) {\r
+        if (preg_match($regexp, $errormsg)) {\r
+            return $code;\r
+        }\r
+    }\r
+    // Fall back to DB_ERROR if there was no mapping.\r
+    return DB_ERROR;\r
+}\r
+       \r
+function adodb_error_odbc()\r
+{\r
+static $MAP = array(\r
+            '01004' => DB_ERROR_TRUNCATED,\r
+            '07001' => DB_ERROR_MISMATCH,\r
+            '21S01' => DB_ERROR_MISMATCH,\r
+            '21S02' => DB_ERROR_MISMATCH,\r
+            '22003' => DB_ERROR_INVALID_NUMBER,\r
+            '22008' => DB_ERROR_INVALID_DATE,\r
+            '22012' => DB_ERROR_DIVZERO,\r
+            '23000' => DB_ERROR_CONSTRAINT,\r
+            '24000' => DB_ERROR_INVALID,\r
+            '34000' => DB_ERROR_INVALID,\r
+            '37000' => DB_ERROR_SYNTAX,\r
+            '42000' => DB_ERROR_SYNTAX,\r
+            'IM001' => DB_ERROR_UNSUPPORTED,\r
+            'S0000' => DB_ERROR_NOSUCHTABLE,\r
+            'S0001' => DB_ERROR_NOT_FOUND,\r
+            'S0002' => DB_ERROR_NOSUCHTABLE,\r
+            'S0011' => DB_ERROR_ALREADY_EXISTS,\r
+            'S0012' => DB_ERROR_NOT_FOUND,\r
+            'S0021' => DB_ERROR_ALREADY_EXISTS,\r
+            'S0022' => DB_ERROR_NOT_FOUND,\r
+                       'S1000' => DB_ERROR_NOSUCHTABLE,\r
+            'S1009' => DB_ERROR_INVALID,\r
+            'S1090' => DB_ERROR_INVALID,\r
+            'S1C00' => DB_ERROR_NOT_CAPABLE\r
+        );\r
+               return $MAP;\r
+}\r
+\r
+function adodb_error_ibase()\r
+{\r
+static $MAP = array(\r
+            -104 => DB_ERROR_SYNTAX,\r
+            -150 => DB_ERROR_ACCESS_VIOLATION,\r
+            -151 => DB_ERROR_ACCESS_VIOLATION,\r
+            -155 => DB_ERROR_NOSUCHTABLE,\r
+            -157 => DB_ERROR_NOSUCHFIELD,\r
+            -158 => DB_ERROR_VALUE_COUNT_ON_ROW,\r
+            -170 => DB_ERROR_MISMATCH,\r
+            -171 => DB_ERROR_MISMATCH,\r
+            -172 => DB_ERROR_INVALID,\r
+            -204 => DB_ERROR_INVALID,\r
+            -205 => DB_ERROR_NOSUCHFIELD,\r
+            -206 => DB_ERROR_NOSUCHFIELD,\r
+            -208 => DB_ERROR_INVALID,\r
+            -219 => DB_ERROR_NOSUCHTABLE,\r
+            -297 => DB_ERROR_CONSTRAINT,\r
+            -530 => DB_ERROR_CONSTRAINT,\r
+            -803 => DB_ERROR_CONSTRAINT,\r
+            -551 => DB_ERROR_ACCESS_VIOLATION,\r
+            -552 => DB_ERROR_ACCESS_VIOLATION,\r
+            -922 => DB_ERROR_NOSUCHDB,\r
+            -923 => DB_ERROR_CONNECT_FAILED,\r
+            -924 => DB_ERROR_CONNECT_FAILED\r
+        );\r
+               \r
+               return $MAP;\r
+}\r
+\r
+function adodb_error_ifx()\r
+{\r
+static $MAP = array(\r
+            '-201'    => DB_ERROR_SYNTAX,\r
+            '-206'    => DB_ERROR_NOSUCHTABLE,\r
+            '-217'    => DB_ERROR_NOSUCHFIELD,\r
+            '-329'    => DB_ERROR_NODBSELECTED,\r
+            '-1204'   => DB_ERROR_INVALID_DATE,\r
+            '-1205'   => DB_ERROR_INVALID_DATE,\r
+            '-1206'   => DB_ERROR_INVALID_DATE,\r
+            '-1209'   => DB_ERROR_INVALID_DATE,\r
+            '-1210'   => DB_ERROR_INVALID_DATE,\r
+            '-1212'   => DB_ERROR_INVALID_DATE\r
+       );\r
+          \r
+          return $MAP;\r
+}\r
+\r
+function adodb_error_oci8()\r
+{\r
+static $MAP = array(\r
+                        1 => DB_ERROR_ALREADY_EXISTS,\r
+            900 => DB_ERROR_SYNTAX,\r
+            904 => DB_ERROR_NOSUCHFIELD,\r
+            923 => DB_ERROR_SYNTAX,\r
+            942 => DB_ERROR_NOSUCHTABLE,\r
+            955 => DB_ERROR_ALREADY_EXISTS,\r
+            1476 => DB_ERROR_DIVZERO,\r
+            1722 => DB_ERROR_INVALID_NUMBER,\r
+            2289 => DB_ERROR_NOSUCHTABLE,\r
+            2291 => DB_ERROR_CONSTRAINT,\r
+            2449 => DB_ERROR_CONSTRAINT\r
+        );\r
+          \r
+       return $MAP;\r
+}\r
+\r
+function adodb_error_mssql()\r
+{\r
+static $MAP = array(\r
+                 208 => DB_ERROR_NOSUCHTABLE,\r
+          2601 => DB_ERROR_ALREADY_EXISTS\r
+       );\r
+          \r
+       return $MAP;\r
+}\r
+\r
+function adodb_error_sqlite()\r
+{\r
+static $MAP = array(\r
+                 1 => DB_ERROR_SYNTAX\r
+       );\r
+          \r
+       return $MAP;\r
+}\r
+\r
+function adodb_error_mysql()\r
+{\r
+static $MAP = array(\r
+           1004 => DB_ERROR_CANNOT_CREATE,\r
+           1005 => DB_ERROR_CANNOT_CREATE,\r
+           1006 => DB_ERROR_CANNOT_CREATE,\r
+           1007 => DB_ERROR_ALREADY_EXISTS,\r
+           1008 => DB_ERROR_CANNOT_DROP,\r
+                  1045 => DB_ERROR_ACCESS_VIOLATION,\r
+           1046 => DB_ERROR_NODBSELECTED,\r
+                  1049 => DB_ERROR_NOSUCHDB,\r
+           1050 => DB_ERROR_ALREADY_EXISTS,\r
+           1051 => DB_ERROR_NOSUCHTABLE,\r
+           1054 => DB_ERROR_NOSUCHFIELD,\r
+           1062 => DB_ERROR_ALREADY_EXISTS,\r
+           1064 => DB_ERROR_SYNTAX,\r
+           1100 => DB_ERROR_NOT_LOCKED,\r
+           1136 => DB_ERROR_VALUE_COUNT_ON_ROW,\r
+           1146 => DB_ERROR_NOSUCHTABLE,\r
+           1048 => DB_ERROR_CONSTRAINT,\r
+                   2002 => DB_ERROR_CONNECT_FAILED,\r
+                       2005 => DB_ERROR_CONNECT_FAILED\r
+       );\r
+          \r
+       return $MAP;\r
+}\r
+?>
\ No newline at end of file
diff --git a/libraries/adodb/adodb-errorhandler.inc.php b/libraries/adodb/adodb-errorhandler.inc.php
new file mode 100755 (executable)
index 0000000..a6f92d5
--- /dev/null
@@ -0,0 +1,79 @@
+<?php\r
+/**\r
+ * @version V4.65 22 July 2005  (c) 2000-2005 John Lim (jlim@natsoft.com.my). All rights reserved.\r
+ * Released under both BSD license and Lesser GPL library license.\r
+ * Whenever there is any discrepancy between the two licenses,\r
+ * the BSD license will take precedence.\r
+ *\r
+ * Set tabs to 4 for best viewing.\r
+ *\r
+ * Latest version is available at http://php.weblogs.com\r
+ *\r
+*/\r
+\r
+\r
+// added Claudio Bustos  clbustos#entelchile.net\r
+if (!defined('ADODB_ERROR_HANDLER_TYPE')) define('ADODB_ERROR_HANDLER_TYPE',E_USER_ERROR); \r
+\r
+if (!defined('ADODB_ERROR_HANDLER')) define('ADODB_ERROR_HANDLER','ADODB_Error_Handler');\r
+\r
+/**\r
+* Default Error Handler. This will be called with the following params\r
+*\r
+* @param $dbms         the RDBMS you are connecting to\r
+* @param $fn           the name of the calling function (in uppercase)\r
+* @param $errno                the native error number from the database\r
+* @param $errmsg       the native error msg from the database\r
+* @param $p1           $fn specific parameter - see below\r
+* @param $p2           $fn specific parameter - see below\r
+* @param $thisConn     $current connection object - can be false if no connection object created\r
+*/\r
+function ADODB_Error_Handler($dbms, $fn, $errno, $errmsg, $p1, $p2, &$thisConnection)\r
+{\r
+       if (error_reporting() == 0) return; // obey @ protocol\r
+       switch($fn) {\r
+       case 'EXECUTE':\r
+               $sql = $p1;\r
+               $inputparams = $p2;\r
+\r
+               $s = "$dbms error: [$errno: $errmsg] in $fn(\"$sql\")\n";\r
+               break;\r
+\r
+       case 'PCONNECT':\r
+       case 'CONNECT':\r
+               $host = $p1;\r
+               $database = $p2;\r
+\r
+               $s = "$dbms error: [$errno: $errmsg] in $fn($host, '****', '****', $database)\n";\r
+               break;\r
+       default:\r
+               $s = "$dbms error: [$errno: $errmsg] in $fn($p1, $p2)\n";\r
+               break;\r
+       }\r
+       /*\r
+       * Log connection error somewhere\r
+       *       0 message is sent to PHP's system logger, using the Operating System's system\r
+       *               logging mechanism or a file, depending on what the error_log configuration\r
+       *               directive is set to.\r
+       *       1 message is sent by email to the address in the destination parameter.\r
+       *               This is the only message type where the fourth parameter, extra_headers is used.\r
+       *               This message type uses the same internal function as mail() does.\r
+       *       2 message is sent through the PHP debugging connection.\r
+       *               This option is only available if remote debugging has been enabled.\r
+       *               In this case, the destination parameter specifies the host name or IP address\r
+       *               and optionally, port number, of the socket receiving the debug information.\r
+       *       3 message is appended to the file destination\r
+       */\r
+       if (defined('ADODB_ERROR_LOG_TYPE')) {\r
+               $t = date('Y-m-d H:i:s');\r
+               if (defined('ADODB_ERROR_LOG_DEST'))\r
+                       error_log("($t) $s", ADODB_ERROR_LOG_TYPE, ADODB_ERROR_LOG_DEST);\r
+               else\r
+                       error_log("($t) $s", ADODB_ERROR_LOG_TYPE);\r
+       }\r
+\r
+\r
+       //print "<p>$s</p>";\r
+       trigger_error($s,ADODB_ERROR_HANDLER_TYPE); \r
+}\r
+?>\r
diff --git a/libraries/adodb/adodb-errorpear.inc.php b/libraries/adodb/adodb-errorpear.inc.php
new file mode 100755 (executable)
index 0000000..011e9a6
--- /dev/null
@@ -0,0 +1,88 @@
+<?php\r
+/** \r
+ * @version V4.65 22 July 2005 (c) 2000-2005 John Lim (jlim@natsoft.com.my). All rights reserved.\r
+ * Released under both BSD license and Lesser GPL library license. \r
+  Whenever there is any discrepancy between the two licenses, \r
+  the BSD license will take precedence. \r
+ *\r
+ * Set tabs to 4 for best viewing.\r
+ * \r
+ * Latest version is available at http://php.weblogs.com\r
+ * \r
+*/\r
+include_once('PEAR.php');\r
+\r
+if (!defined('ADODB_ERROR_HANDLER')) define('ADODB_ERROR_HANDLER','ADODB_Error_PEAR');\r
+\r
+/*\r
+* Enabled the following if you want to terminate scripts when an error occurs\r
+*/\r
+//PEAR::setErrorHandling (PEAR_ERROR_DIE);\r
+\r
+/*\r
+* Name of the PEAR_Error derived class to call.\r
+*/\r
+if (!defined('ADODB_PEAR_ERROR_CLASS')) define('ADODB_PEAR_ERROR_CLASS','PEAR_Error');\r
+\r
+/*\r
+* Store the last PEAR_Error object here\r
+*/\r
+global $ADODB_Last_PEAR_Error; $ADODB_Last_PEAR_Error = false;\r
+\r
+  /**\r
+* Error Handler with PEAR support. This will be called with the following params\r
+*\r
+* @param $dbms         the RDBMS you are connecting to\r
+* @param $fn           the name of the calling function (in uppercase)\r
+* @param $errno                the native error number from the database \r
+* @param $errmsg       the native error msg from the database\r
+* @param $p1           $fn specific parameter - see below\r
+* @param $P2           $fn specific parameter - see below\r
+       */\r
+function ADODB_Error_PEAR($dbms, $fn, $errno, $errmsg, $p1=false, $p2=false)\r
+{\r
+global $ADODB_Last_PEAR_Error;\r
+       \r
+       if (error_reporting() == 0) return; // obey @ protocol\r
+       switch($fn) {\r
+       case 'EXECUTE':\r
+               $sql = $p1;\r
+               $inputparams = $p2;\r
+               \r
+               $s = "$dbms error: [$errno: $errmsg] in $fn(\"$sql\")";\r
+               break;\r
+               \r
+       case 'PCONNECT':\r
+       case 'CONNECT':\r
+               $host = $p1;\r
+               $database = $p2;\r
+               \r
+               $s = "$dbms error: [$errno: $errmsg] in $fn('$host', ?, ?, '$database')";\r
+               break;\r
+               \r
+       default:\r
+               $s = "$dbms error: [$errno: $errmsg] in $fn($p1, $p2)";\r
+               break;\r
+       }\r
+       \r
+       $class = ADODB_PEAR_ERROR_CLASS;\r
+       $ADODB_Last_PEAR_Error = new $class($s, $errno,\r
+               $GLOBALS['_PEAR_default_error_mode'],\r
+               $GLOBALS['_PEAR_default_error_options'], \r
+               $errmsg);\r
+               \r
+       //print "<p>!$s</p>";\r
+}\r
+\r
+/**\r
+* Returns last PEAR_Error object. This error might be for an error that\r
+* occured several sql statements ago.\r
+*/\r
+function &ADODB_PEAR_Error()\r
+{\r
+global $ADODB_Last_PEAR_Error;\r
+\r
+       return $ADODB_Last_PEAR_Error;\r
+}\r
+               \r
+?>
\ No newline at end of file
diff --git a/libraries/adodb/adodb-exceptions.inc.php b/libraries/adodb/adodb-exceptions.inc.php
new file mode 100755 (executable)
index 0000000..693b049
--- /dev/null
@@ -0,0 +1,80 @@
+<?php\r
+\r
+/**\r
+ * @version V4.65 22 July 2005  (c) 2000-2005 John Lim (jlim@natsoft.com.my). All rights reserved.\r
+ * Released under both BSD license and Lesser GPL library license.\r
+ * Whenever there is any discrepancy between the two licenses,\r
+ * the BSD license will take precedence.\r
+ *\r
+ * Set tabs to 4 for best viewing.\r
+ *\r
+ * Latest version is available at http://php.weblogs.com\r
+ *\r
+ * Exception-handling code using PHP5 exceptions (try-catch-throw).\r
+ */\r
+\r
+\r
+if (!defined('ADODB_ERROR_HANDLER_TYPE')) define('ADODB_ERROR_HANDLER_TYPE',E_USER_ERROR); \r
+define('ADODB_ERROR_HANDLER','adodb_throw');\r
+\r
+class ADODB_Exception extends Exception {\r
+var $dbms;\r
+var $fn;\r
+var $sql = '';\r
+var $params = '';\r
+var $host = '';\r
+var $database = '';\r
+       \r
+       function __construct($dbms, $fn, $errno, $errmsg, $p1, $p2, $thisConnection)\r
+       {\r
+               switch($fn) {\r
+               case 'EXECUTE':\r
+                       $this->sql = $p1;\r
+                       $this->params = $p2;\r
+                       $s = "$dbms error: [$errno: $errmsg] in $fn(\"$p1\")\n";\r
+                       break;\r
+       \r
+               case 'PCONNECT':\r
+               case 'CONNECT':\r
+                       $user = $thisConnection->user;\r
+                       $s = "$dbms error: [$errno: $errmsg] in $fn($p1, '$user', '****', $p2)\n";\r
+                       break;\r
+               default:\r
+                       $s = "$dbms error: [$errno: $errmsg] in $fn($p1, $p2)\n";\r
+                       break;\r
+               }\r
+       \r
+               $this->dbms = $dbms;\r
+               $this->host = $thisConnection->host;\r
+               $this->database = $thisConnection->database;\r
+               $this->fn = $fn;\r
+               $this->msg = $errmsg;\r
+                               \r
+               if (!is_numeric($errno)) $errno = -1;\r
+               parent::__construct($s,$errno);\r
+       }\r
+}\r
+\r
+/**\r
+* Default Error Handler. This will be called with the following params\r
+*\r
+* @param $dbms         the RDBMS you are connecting to\r
+* @param $fn           the name of the calling function (in uppercase)\r
+* @param $errno                the native error number from the database\r
+* @param $errmsg       the native error msg from the database\r
+* @param $p1           $fn specific parameter - see below\r
+* @param $P2           $fn specific parameter - see below\r
+*/\r
+\r
+function adodb_throw($dbms, $fn, $errno, $errmsg, $p1, $p2, $thisConnection)\r
+{\r
+global $ADODB_EXCEPTION;\r
+       \r
+       if (error_reporting() == 0) return; // obey @ protocol\r
+       if (is_string($ADODB_EXCEPTION)) $errfn = $ADODB_EXCEPTION;\r
+       else $errfn = 'ADODB_EXCEPTION';\r
+       throw new $errfn($dbms, $fn, $errno, $errmsg, $p1, $p2, $thisConnection);\r
+}\r
+\r
+\r
+?>
\ No newline at end of file
diff --git a/libraries/adodb/adodb-iterator.inc.php b/libraries/adodb/adodb-iterator.inc.php
new file mode 100755 (executable)
index 0000000..a7126dd
--- /dev/null
@@ -0,0 +1,84 @@
+<?php\r
+\r
+/*\r
+  V4.65 22 July 2005  (c) 2000-2005 John Lim (jlim@natsoft.com.my). All rights reserved.\r
+  Released under both BSD license and Lesser GPL library license. \r
+  Whenever there is any discrepancy between the two licenses, \r
+  the BSD license will take precedence.\r
+  \r
+  Set tabs to 4.\r
+  \r
+  Declares the ADODB Base Class for PHP5 "ADODB_BASE_RS", and supports iteration with \r
+  the ADODB_Iterator class.\r
+  \r
+               $rs = $db->Execute("select * from adoxyz");\r
+               foreach($rs as $k => $v) {\r
+                       echo $k; print_r($v); echo "<br>";\r
+               }\r
+               \r
+               \r
+       Iterator code based on http://cvs.php.net/cvs.php/php-src/ext/spl/examples/cachingiterator.inc?login=2\r
+ */\r
\r
+\r
+ class ADODB_Iterator implements Iterator {\r
+\r
+    private $rs;\r
+\r
+    function __construct($rs) \r
+       {\r
+        $this->rs = $rs;\r
+    }\r
+    function rewind() \r
+       {\r
+        $this->rs->MoveFirst();\r
+    }\r
+\r
+       function valid() \r
+       {\r
+        return !$this->rs->EOF;\r
+    }\r
+       \r
+    function key() \r
+       {\r
+        return $this->rs->_currentRow;\r
+    }\r
+       \r
+    function current() \r
+       {\r
+        return $this->rs->fields;\r
+    }\r
+       \r
+    function next() \r
+       {\r
+        $this->rs->MoveNext();\r
+    }\r
+       \r
+       function __call($func, $params)\r
+       {\r
+               return call_user_func_array(array($this->rs, $func), $params);\r
+       }\r
+\r
+       \r
+       function hasMore()\r
+       {\r
+               return !$this->rs->EOF;\r
+       }\r
+\r
+}\r
+\r
+\r
+class ADODB_BASE_RS implements IteratorAggregate {\r
+    function getIterator() {\r
+        return new ADODB_Iterator($this);\r
+    }\r
+       \r
+       /* this is experimental - i don't really know what to return... */\r
+       function __toString()\r
+       {\r
+               include_once(ADODB_DIR.'/toexport.inc.php');\r
+               return _adodb_export($this,',',',',false,true);\r
+       }\r
+} \r
+\r
+?>
\ No newline at end of file
diff --git a/libraries/adodb/adodb-lib.inc.php b/libraries/adodb/adodb-lib.inc.php
new file mode 100644 (file)
index 0000000..920ea38
--- /dev/null
@@ -0,0 +1,1004 @@
+<?php
+
+// security - hide paths
+if (!defined('ADODB_DIR')) die();
+
+global $ADODB_INCLUDED_LIB;
+$ADODB_INCLUDED_LIB = 1;
+
+/* 
+ @version V4.65 22 July 2005 (c) 2000-2005 John Lim (jlim\@natsoft.com.my). All rights reserved.
+  Released under both BSD license and Lesser GPL library license. 
+  Whenever there is any discrepancy between the two licenses, 
+  the BSD license will take precedence. See License.txt. 
+  Set tabs to 4 for best viewing.
+  
+  Less commonly used functions are placed here to reduce size of adodb.inc.php. 
+*/ 
+
+
+// Force key to upper. 
+// See also http://www.php.net/manual/en/function.array-change-key-case.php
+function _array_change_key_case($an_array)
+{
+       if (is_array($an_array)) {
+               $new_array = array();
+               foreach($an_array as $key=>$value)
+                       $new_array[strtoupper($key)] = $value;
+
+               return $new_array;
+   }
+
+       return $an_array;
+}
+
+function _adodb_replace(&$zthis, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc)
+{
+               if (count($fieldArray) == 0) return 0;
+               $first = true;
+               $uSet = '';
+               
+               if (!is_array($keyCol)) {
+                       $keyCol = array($keyCol);
+               }
+               foreach($fieldArray as $k => $v) {
+                       if ($autoQuote && !is_numeric($v) and strncmp($v,"'",1) !== 0 and strcasecmp($v,'null')!=0) {
+                               $v = $zthis->qstr($v);
+                               $fieldArray[$k] = $v;
+                       }
+                       if (in_array($k,$keyCol)) continue; // skip UPDATE if is key
+                       
+                       if ($first) {
+                               $first = false;                 
+                               $uSet = "$k=$v";
+                       } else
+                               $uSet .= ",$k=$v";
+               }
+                
+               $where = false;
+               foreach ($keyCol as $v) {
+                       if ($where) $where .= " and $v=$fieldArray[$v]";
+                       else $where = "$v=$fieldArray[$v]";
+               }
+               
+               if ($uSet && $where) {
+                       $update = "UPDATE $table SET $uSet WHERE $where";
+
+                       $rs = $zthis->Execute($update);
+                       
+                       
+                       if ($rs) {
+                               if ($zthis->poorAffectedRows) {
+                               /*
+                                The Select count(*) wipes out any errors that the update would have returned. 
+                               http://phplens.com/lens/lensforum/msgs.php?id=5696
+                               */
+                                       if ($zthis->ErrorNo()<>0) return 0;
+                                       
+                               # affected_rows == 0 if update field values identical to old values
+                               # for mysql - which is silly. 
+                       
+                                       $cnt = $zthis->GetOne("select count(*) from $table where $where");
+                                       if ($cnt > 0) return 1; // record already exists
+                               } else {
+                                       if (($zthis->Affected_Rows()>0)) return 1;
+                               }
+                       } else
+                               return 0;
+               }
+               
+       //      print "<p>Error=".$this->ErrorNo().'<p>';
+               $first = true;
+               foreach($fieldArray as $k => $v) {
+                       if ($has_autoinc && in_array($k,$keyCol)) continue; // skip autoinc col
+                       
+                       if ($first) {
+                               $first = false;                 
+                               $iCols = "$k";
+                               $iVals = "$v";
+                       } else {
+                               $iCols .= ",$k";
+                               $iVals .= ",$v";
+                       }                               
+               }
+               $insert = "INSERT INTO $table ($iCols) VALUES ($iVals)"; 
+               $rs = $zthis->Execute($insert);
+               return ($rs) ? 2 : 0;
+}
+
+// Requires $ADODB_FETCH_MODE = ADODB_FETCH_NUM
+function _adodb_getmenu(&$zthis, $name,$defstr='',$blank1stItem=true,$multiple=false,
+                       $size=0, $selectAttr='',$compareFields0=true)
+{
+       $hasvalue = false;
+
+       if ($multiple or is_array($defstr)) {
+               if ($size==0) $size=5;
+               $attr = ' multiple size="'.$size.'"';
+               if (!strpos($name,'[]')) $name .= '[]';
+       } else if ($size) $attr = ' size="'.$size.'"';
+       else $attr ='';
+       
+       $s = '<select name="'.$name.'"'.$attr.' '.$selectAttr.'>';
+       if ($blank1stItem) 
+               if (is_string($blank1stItem))  {
+                       $barr = explode(':',$blank1stItem);
+                       if (sizeof($barr) == 1) $barr[] = '';
+                       $s .= "\n<option value=\"".$barr[0]."\">".$barr[1]."</option>";
+               } else $s .= "\n<option></option>";
+
+       if ($zthis->FieldCount() > 1) $hasvalue=true;
+       else $compareFields0 = true;
+       
+       $value = '';
+    $optgroup = null;
+    $firstgroup = true;
+    $fieldsize = $zthis->FieldCount();
+       while(!$zthis->EOF) {
+               $zval = rtrim(reset($zthis->fields));
+
+               if ($blank1stItem && $zval=="") {
+                       $zthis->MoveNext();
+                       continue;
+               }
+
+        if ($fieldsize > 1) {
+                       if (isset($zthis->fields[1]))
+                               $zval2 = rtrim($zthis->fields[1]);
+                       else
+                               $zval2 = rtrim(next($zthis->fields));
+               }
+               $selected = ($compareFields0) ? $zval : $zval2;
+               
+        $group = '';
+               if ($fieldsize > 2) {
+            $group = rtrim($zthis->fields[2]);
+        }
+        if ($optgroup != $group) {
+            $optgroup = $group;
+            if ($firstgroup) {
+                $firstgroup = false;
+                $s .="\n<optgroup label='". htmlspecialchars($group) ."'>";
+            } else {
+                $s .="\n</optgroup>";
+                $s .="\n<optgroup label='". htmlspecialchars($group) ."'>";
+            }
+               }
+       
+               if ($hasvalue) 
+                       $value = " value='".htmlspecialchars($zval2)."'";
+               
+               if (is_array($defstr))  {
+                       
+                       if (in_array($selected,$defstr)) 
+                               $s .= "\n<option selected='selected'$value>".htmlspecialchars($zval).'</option>';
+                       else 
+                               $s .= "\n<option".$value.'>'.htmlspecialchars($zval).'</option>';
+               }
+               else {
+                       if (strcasecmp($selected,$defstr)==0) 
+                               $s .= "\n<option selected='selected'$value>".htmlspecialchars($zval).'</option>';
+                       else
+                               $s .= "\n<option".$value.'>'.htmlspecialchars($zval).'</option>';
+               }
+               $zthis->MoveNext();
+       } // while
+       
+    // closing last optgroup
+    if($optgroup != null) {
+        $s .= "\n</optgroup>";
+       }
+       return $s ."\n</select>\n";
+}
+
+// Requires $ADODB_FETCH_MODE = ADODB_FETCH_NUM
+function _adodb_getmenu_gp(&$zthis, $name,$defstr='',$blank1stItem=true,$multiple=false,
+                       $size=0, $selectAttr='',$compareFields0=true)
+{
+       $hasvalue = false;
+
+       if ($multiple or is_array($defstr)) {
+               if ($size==0) $size=5;
+               $attr = ' multiple size="'.$size.'"';
+               if (!strpos($name,'[]')) $name .= '[]';
+       } else if ($size) $attr = ' size="'.$size.'"';
+       else $attr ='';
+       
+       $s = '<select name="'.$name.'"'.$attr.' '.$selectAttr.'>';
+       if ($blank1stItem) 
+               if (is_string($blank1stItem))  {
+                       $barr = explode(':',$blank1stItem);
+                       if (sizeof($barr) == 1) $barr[] = '';
+                       $s .= "\n<option value=\"".$barr[0]."\">".$barr[1]."</option>";
+               } else $s .= "\n<option></option>";
+
+       if ($zthis->FieldCount() > 1) $hasvalue=true;
+       else $compareFields0 = true;
+       
+       $value = '';
+    $optgroup = null;
+    $firstgroup = true;
+    $fieldsize = sizeof($zthis->fields);
+       while(!$zthis->EOF) {
+               $zval = rtrim(reset($zthis->fields));
+
+               if ($blank1stItem && $zval=="") {
+                       $zthis->MoveNext();
+                       continue;
+               }
+
+        if ($fieldsize > 1) {
+                       if (isset($zthis->fields[1]))
+                               $zval2 = rtrim($zthis->fields[1]);
+                       else
+                               $zval2 = rtrim(next($zthis->fields));
+               }
+               $selected = ($compareFields0) ? $zval : $zval2;
+               
+        $group = '';
+               if (isset($zthis->fields[2])) {
+            $group = rtrim($zthis->fields[2]);
+        }
+        if ($optgroup != $group) {
+            $optgroup = $group;
+            if ($firstgroup) {
+                $firstgroup = false;
+                $s .="\n<optgroup label='". htmlspecialchars($group) ."'>";
+            } else {
+                $s .="\n</optgroup>";
+                $s .="\n<optgroup label='". htmlspecialchars($group) ."'>";
+            }
+               }
+       
+               if ($hasvalue) 
+                       $value = " value='".htmlspecialchars($zval2)."'";
+               
+               if (is_array($defstr))  {
+                       
+                       if (in_array($selected,$defstr)) 
+                               $s .= "\n<option selected='selected'$value>".htmlspecialchars($zval).'</option>';
+                       else 
+                               $s .= "\n<option".$value.'>'.htmlspecialchars($zval).'</option>';
+               }
+               else {
+                       if (strcasecmp($selected,$defstr)==0) 
+                               $s .= "\n<option selected='selected'$value>".htmlspecialchars($zval).'</option>';
+                       else
+                               $s .= "\n<option".$value.'>'.htmlspecialchars($zval).'</option>';
+               }
+               $zthis->MoveNext();
+       } // while
+       
+    // closing last optgroup
+    if($optgroup != null) {
+        $s .= "\n</optgroup>";
+       }
+       return $s ."\n</select>\n";
+}
+
+
+/*
+       Count the number of records this sql statement will return by using
+       query rewriting techniques...
+       
+       Does not work with UNIONs, except with postgresql and oracle.
+*/
+function _adodb_getcount(&$zthis, $sql,$inputarr=false,$secs2cache=0) 
+{
+       $qryRecs = 0;
+       
+        if (preg_match("/^\s*SELECT\s+DISTINCT/is", $sql) || 
+               preg_match('/\s+GROUP\s+BY\s+/is',$sql) || 
+               preg_match('/\s+UNION\s+/is',$sql)) {
+               // ok, has SELECT DISTINCT or GROUP BY so see if we can use a table alias
+               // but this is only supported by oracle and postgresql...
+               if ($zthis->dataProvider == 'oci8') {
+                       
+                       $rewritesql = preg_replace('/(\sORDER\s+BY\s.*)/is','',$sql);
+                       
+                       // Allow Oracle hints to be used for query optimization, Chris Wrye
+                       if (preg_match('#/\\*+.*?\\*\\/#', $sql, $hint)) {
+                               $rewritesql = "SELECT ".$hint[0]." COUNT(*) FROM (".$rewritesql.")"; 
+                       } else
+                               $rewritesql = "SELECT COUNT(*) FROM (".$rewritesql.")"; 
+                       
+               } else if (strncmp($zthis->databaseType,'postgres',8) == 0)  {
+                       
+                       $info = $zthis->ServerInfo();
+                       if (substr($info['version'],0,3) >= 7.1) { // good till version 999
+                               $rewritesql = preg_replace('/(\sORDER\s+BY\s[^)]*)/is','',$sql);
+                               $rewritesql = "SELECT COUNT(*) FROM ($rewritesql) _ADODB_ALIAS_";
+                       }
+               }
+       } else {
+               // now replace SELECT ... FROM with SELECT COUNT(*) FROM
+               $rewritesql = preg_replace(
+                                       '/^\s*SELECT\s.*\s+FROM\s/Uis','SELECT COUNT(*) FROM ',$sql);
+
+               // fix by alexander zhukov, alex#unipack.ru, because count(*) and 'order by' fails 
+               // with mssql, access and postgresql. Also a good speedup optimization - skips sorting!
+               $rewritesql = preg_replace('/(\sORDER\s+BY\s[^)]*)/is','',$rewritesql);
+       }
+       
+       if (isset($rewritesql) && $rewritesql != $sql) {
+               if ($secs2cache) {
+                       // we only use half the time of secs2cache because the count can quickly
+                       // become inaccurate if new records are added
+                       $qryRecs = $zthis->CacheGetOne($secs2cache/2,$rewritesql,$inputarr);
+                       
+               } else {
+                       $qryRecs = $zthis->GetOne($rewritesql,$inputarr);
+               }
+               if ($qryRecs !== false) return $qryRecs;
+       }
+       //--------------------------------------------
+       // query rewrite failed - so try slower way...
+       
+       
+       // strip off unneeded ORDER BY if no UNION
+       if (preg_match('/\s*UNION\s*/is', $sql)) $rewritesql = $sql;
+       else $rewritesql = preg_replace('/(\sORDER\s+BY\s.*)/is','',$sql); 
+       
+       $rstest = &$zthis->Execute($rewritesql,$inputarr);
+       if (!$rstest) $rstest = $zthis->Execute($sql,$inputarr);
+       
+       if ($rstest) {
+                       $qryRecs = $rstest->RecordCount();
+               if ($qryRecs == -1) { 
+               global $ADODB_EXTENSION;
+               // some databases will return -1 on MoveLast() - change to MoveNext()
+                       if ($ADODB_EXTENSION) {
+                               while(!$rstest->EOF) {
+                                       adodb_movenext($rstest);
+                               }
+                       } else {
+                               while(!$rstest->EOF) {
+                                       $rstest->MoveNext();
+                               }
+                       }
+                       $qryRecs = $rstest->_currentRow;
+               }
+               $rstest->Close();
+               if ($qryRecs == -1) return 0;
+       }
+       
+       return $qryRecs;
+}
+
+/*
+       Code originally from "Cornel G" <conyg@fx.ro>
+
+       This code might not work with SQL that has UNION in it  
+       
+       Also if you are using CachePageExecute(), there is a strong possibility that
+       data will get out of synch. use CachePageExecute() only with tables that
+       rarely change.
+*/
+function &_adodb_pageexecute_all_rows(&$zthis, $sql, $nrows, $page, 
+                                               $inputarr=false, $secs2cache=0) 
+{
+       $atfirstpage = false;
+       $atlastpage = false;
+       $lastpageno=1;
+
+       // If an invalid nrows is supplied, 
+       // we assume a default value of 10 rows per page
+       if (!isset($nrows) || $nrows <= 0) $nrows = 10;
+
+       $qryRecs = false; //count records for no offset
+       
+       $qryRecs = _adodb_getcount($zthis,$sql,$inputarr,$secs2cache);
+       $lastpageno = (int) ceil($qryRecs / $nrows);
+       $zthis->_maxRecordCount = $qryRecs;
+       
+
+
+       // ***** Here we check whether $page is the last page or 
+       // whether we are trying to retrieve 
+       // a page number greater than the last page number.
+       if ($page >= $lastpageno) {
+               $page = $lastpageno;
+               $atlastpage = true;
+       }
+       
+       // If page number <= 1, then we are at the first page
+       if (empty($page) || $page <= 1) {       
+               $page = 1;
+               $atfirstpage = true;
+       }
+       
+       // We get the data we want
+       $offset = $nrows * ($page-1);
+       if ($secs2cache > 0) 
+               $rsreturn = &$zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $offset, $inputarr);
+       else 
+               $rsreturn = &$zthis->SelectLimit($sql, $nrows, $offset, $inputarr, $secs2cache);
+
+       
+       // Before returning the RecordSet, we set the pagination properties we need
+       if ($rsreturn) {
+               $rsreturn->_maxRecordCount = $qryRecs;
+               $rsreturn->rowsPerPage = $nrows;
+               $rsreturn->AbsolutePage($page);
+               $rsreturn->AtFirstPage($atfirstpage);
+               $rsreturn->AtLastPage($atlastpage);
+               $rsreturn->LastPageNo($lastpageno);
+       }
+       return $rsreturn;
+}
+
+// Iván Oliva version
+function &_adodb_pageexecute_no_last_page(&$zthis, $sql, $nrows, $page, $inputarr=false, $secs2cache=0) 
+{
+
+       $atfirstpage = false;
+       $atlastpage = false;
+       
+       if (!isset($page) || $page <= 1) {      // If page number <= 1, then we are at the first page
+               $page = 1;
+               $atfirstpage = true;
+       }
+       if ($nrows <= 0) $nrows = 10;   // If an invalid nrows is supplied, we assume a default value of 10 rows per page
+       
+       // ***** Here we check whether $page is the last page or whether we are trying to retrieve a page number greater than 
+       // the last page number.
+       $pagecounter = $page + 1;
+       $pagecounteroffset = ($pagecounter * $nrows) - $nrows;
+       if ($secs2cache>0) $rstest = &$zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $pagecounteroffset, $inputarr);
+       else $rstest = &$zthis->SelectLimit($sql, $nrows, $pagecounteroffset, $inputarr, $secs2cache);
+       if ($rstest) {
+               while ($rstest && $rstest->EOF && $pagecounter>0) {
+                       $atlastpage = true;
+                       $pagecounter--;
+                       $pagecounteroffset = $nrows * ($pagecounter - 1);
+                       $rstest->Close();
+                       if ($secs2cache>0) $rstest = &$zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $pagecounteroffset, $inputarr);
+                       else $rstest = &$zthis->SelectLimit($sql, $nrows, $pagecounteroffset, $inputarr, $secs2cache);
+               }
+               if ($rstest) $rstest->Close();
+       }
+       if ($atlastpage) {      // If we are at the last page or beyond it, we are going to retrieve it
+               $page = $pagecounter;
+               if ($page == 1) $atfirstpage = true;    // We have to do this again in case the last page is the same as the first
+                       //... page, that is, the recordset has only 1 page.
+       }
+       
+       // We get the data we want
+       $offset = $nrows * ($page-1);
+       if ($secs2cache > 0) $rsreturn = &$zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $offset, $inputarr);
+       else $rsreturn = &$zthis->SelectLimit($sql, $nrows, $offset, $inputarr, $secs2cache);
+       
+       // Before returning the RecordSet, we set the pagination properties we need
+       if ($rsreturn) {
+               $rsreturn->rowsPerPage = $nrows;
+               $rsreturn->AbsolutePage($page);
+               $rsreturn->AtFirstPage($atfirstpage);
+               $rsreturn->AtLastPage($atlastpage);
+       }
+       return $rsreturn;
+}
+
+function _adodb_getupdatesql(&$zthis,&$rs, $arrFields,$forceUpdate=false,$magicq=false,$force=2)
+{
+               if (!$rs) {
+                       printf(ADODB_BAD_RS,'GetUpdateSQL');
+                       return false;
+               }
+       
+               $fieldUpdatedCount = 0;
+               $arrFields = _array_change_key_case($arrFields);
+
+               $hasnumeric = isset($rs->fields[0]);
+               $setFields = '';
+               
+               // Loop through all of the fields in the recordset
+               for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) {
+                       // Get the field from the recordset
+                       $field = $rs->FetchField($i);
+
+                       // If the recordset field is one
+                       // of the fields passed in then process.
+                       $upperfname = strtoupper($field->name);
+                       if (adodb_key_exists($upperfname,$arrFields,$force)) {
+                               
+                               // If the existing field value in the recordset
+                               // is different from the value passed in then
+                               // go ahead and append the field name and new value to
+                               // the update query.
+                               
+                               if ($hasnumeric) $val = $rs->fields[$i];
+                               else if (isset($rs->fields[$upperfname])) $val = $rs->fields[$upperfname];
+                               else if (isset($rs->fields[$field->name])) $val =  $rs->fields[$field->name];
+                               else if (isset($rs->fields[strtolower($upperfname)])) $val =  $rs->fields[strtolower($upperfname)];
+                               else $val = '';
+                               
+                       
+                               if ($forceUpdate || strcmp($val, $arrFields[$upperfname])) {
+                                       // Set the counter for the number of fields that will be updated.
+                                       $fieldUpdatedCount++;
+
+                                       // Based on the datatype of the field
+                                       // Format the value properly for the database
+                                       $type = $rs->MetaType($field->type);
+                                               
+
+                                       if ($type == 'null') {
+                                               $type = 'C';
+                                       }
+                                       
+                                       if (strpos($upperfname,' ') !== false)
+                                               $fnameq = $zthis->nameQuote.$upperfname.$zthis->nameQuote;
+                                       else
+                                               $fnameq = $upperfname;
+                                       
+                                       
+                // is_null requires php 4.0.4
+                //********************************************************//
+                if (is_null($arrFields[$upperfname])
+                                       || (empty($arrFields[$upperfname]) && strlen($arrFields[$upperfname]) == 0)
+                    || $arrFields[$upperfname] === 'null'
+                    )
+                {
+                    switch ($force) {
+
+                        //case 0:
+                        //    //Ignore empty values. This is allready handled in "adodb_key_exists" function.
+                        //break;
+
+                        case 1:
+                            //Set null
+                            $setFields .= $field->name . " = null, ";
+                        break;
+                                                       
+                        case 2:
+                            //Set empty
+                            $arrFields[$upperfname] = "";
+                            $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq,$arrFields, $magicq);
+                        break;
+                                               default:
+                        case 3:
+                            //Set the value that was given in array, so you can give both null and empty values
+                            if (is_null($arrFields[$upperfname]) || $arrFields[$upperfname] === 'null') {
+                                $setFields .= $field->name . " = null, ";
+                            } else {
+                                $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq,$arrFields, $magicq);
+                            }
+                        break;
+                    }
+                //********************************************************//
+                } else {
+                                               //we do this so each driver can customize the sql for
+                                               //DB specific column types. 
+                                               //Oracle needs BLOB types to be handled with a returning clause
+                                               //postgres has special needs as well
+                                               $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq,
+                                                                                                                 $arrFields, $magicq);
+                                       }
+                               }
+                       }
+               }
+
+               // If there were any modified fields then build the rest of the update query.
+               if ($fieldUpdatedCount > 0 || $forceUpdate) {
+                                       // Get the table name from the existing query.
+                       if (!empty($rs->tableName)) $tableName = $rs->tableName;
+                       else {
+                               preg_match("/FROM\s+".ADODB_TABLE_REGEX."/is", $rs->sql, $tableName);
+                               $tableName = $tableName[1];
+                       }
+                       // Get the full where clause excluding the word "WHERE" from
+                       // the existing query.
+                       preg_match('/\sWHERE\s(.*)/is', $rs->sql, $whereClause);
+                       
+                       $discard = false;
+                       // not a good hack, improvements?
+                       if ($whereClause) {
+                               if (preg_match('/\s(ORDER\s.*)/is', $whereClause[1], $discard));
+                               else if (preg_match('/\s(LIMIT\s.*)/is', $whereClause[1], $discard));
+                               else preg_match('/\s(FOR UPDATE.*)/is', $whereClause[1], $discard);
+                       } else
+                               $whereClause = array(false,false);
+                               
+                       if ($discard)
+                               $whereClause[1] = substr($whereClause[1], 0, strlen($whereClause[1]) - strlen($discard[1]));
+                       
+                       $sql = 'UPDATE '.$tableName.' SET '.substr($setFields, 0, -2);
+                       if (strlen($whereClause[1]) > 0) 
+                               $sql .= ' WHERE '.$whereClause[1];
+
+                       return $sql;
+
+               } else {
+                       return false;
+       }
+}
+
+function adodb_key_exists($key, &$arr,$force=2)
+{
+       if ($force<=0) {
+               // the following is the old behaviour where null or empty fields are ignored
+               return (!empty($arr[$key])) || (isset($arr[$key]) && strlen($arr[$key])>0);
+       }
+
+       if (isset($arr[$key])) return true;
+       ## null check below
+       if (ADODB_PHPVER >= 0x4010) return array_key_exists($key,$arr);
+       return false;
+}
+
+/**
+ * There is a special case of this function for the oci8 driver.
+ * The proper way to handle an insert w/ a blob in oracle requires
+ * a returning clause with bind variables and a descriptor blob.
+ * 
+ * 
+ */
+function _adodb_getinsertsql(&$zthis,&$rs,$arrFields,$magicq=false,$force=2)
+{
+static $cacheRS = false;
+static $cacheSig = 0;
+static $cacheCols;
+
+       $tableName = '';
+       $values = '';
+       $fields = '';
+       $recordSet = null;
+       $arrFields = _array_change_key_case($arrFields);
+       $fieldInsertedCount = 0;
+       
+       if (is_string($rs)) {
+               //ok we have a table name
+               //try and get the column info ourself.
+               $tableName = $rs;                       
+       
+               //we need an object for the recordSet
+               //because we have to call MetaType.
+               //php can't do a $rsclass::MetaType()
+               $rsclass = $zthis->rsPrefix.$zthis->databaseType;
+               $recordSet = new $rsclass(-1,$zthis->fetchMode);
+               $recordSet->connection = &$zthis;
+               
+               if (is_string($cacheRS) && $cacheRS == $rs) {
+                       $columns =& $cacheCols;
+               } else {
+                       $columns = $zthis->MetaColumns( $tableName );
+                       $cacheRS = $tableName;
+                       $cacheCols = $columns;
+               }
+       } else if (is_subclass_of($rs, 'adorecordset')) {
+               if (isset($rs->insertSig) && is_integer($cacheRS) && $cacheRS == $rs->insertSig) {
+                       $columns =& $cacheCols;
+               } else {
+                       for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) 
+                               $columns[] = $rs->FetchField($i);
+                       $cacheRS = $cacheSig;
+                       $cacheCols = $columns;
+                       $rs->insertSig = $cacheSig++;
+               }
+               $recordSet =& $rs;
+       
+       } else {
+               printf(ADODB_BAD_RS,'GetInsertSQL');
+               return false;
+       }
+
+       // Loop through all of the fields in the recordset
+       foreach( $columns as $field ) { 
+               $upperfname = strtoupper($field->name);
+               if (adodb_key_exists($upperfname,$arrFields,$force)) {
+                       $bad = false;
+                       if (strpos($upperfname,' ') !== false)
+                               $fnameq = $zthis->nameQuote.$upperfname.$zthis->nameQuote;
+                       else
+                               $fnameq = $upperfname;
+                       
+                       $type = $recordSet->MetaType($field->type);
+                       
+            /********************************************************/
+            if (is_null($arrFields[$upperfname])
+                || (empty($arrFields[$upperfname]) && strlen($arrFields[$upperfname]) == 0)
+                || $arrFields[$upperfname] === 'null'
+                               )
+               {
+                    switch ($force) {
+
+                        case 0: // we must always set null if missing
+                                                       $bad = true;
+                                                       break;
+                                                       
+                        case 1:
+                            $values  .= "null, ";
+                        break;
+               
+                        case 2:
+                            //Set empty
+                            $arrFields[$upperfname] = "";
+                            $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq,$arrFields, $magicq);
+                        break;
+
+                                               default:
+                        case 3:
+                            //Set the value that was given in array, so you can give both null and empty values
+                                                       if (is_null($arrFields[$upperfname]) || $arrFields[$upperfname] === 'null') { 
+                                                               $values  .= "null, ";
+                                                       } else {
+                                       $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq, $arrFields, $magicq);
+                                       }
+                               break;
+                       } // switch
+
+            /*********************************************************/
+                       } else {
+                               //we do this so each driver can customize the sql for
+                               //DB specific column types. 
+                               //Oracle needs BLOB types to be handled with a returning clause
+                               //postgres has special needs as well
+                               $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq,
+                                                                                          $arrFields, $magicq);
+                       }
+                       
+                       if ($bad) continue;
+                       // Set the counter for the number of fields that will be inserted.
+                       $fieldInsertedCount++;
+                       
+                       
+                       // Get the name of the fields to insert
+                       $fields .= $fnameq . ", ";
+               }
+       }
+
+
+       // If there were any inserted fields then build the rest of the insert query.
+       if ($fieldInsertedCount <= 0)  return false;
+       
+       // Get the table name from the existing query.
+       if (!$tableName) {
+               if (!empty($rs->tableName)) $tableName = $rs->tableName;
+               else if (preg_match("/FROM\s+".ADODB_TABLE_REGEX."/is", $rs->sql, $tableName))
+                       $tableName = $tableName[1];
+               else 
+                       return false;
+       }               
+
+       // Strip off the comma and space on the end of both the fields
+       // and their values.
+       $fields = substr($fields, 0, -2);
+       $values = substr($values, 0, -2);
+
+       // Append the fields and their values to the insert query.
+       return 'INSERT INTO '.$tableName.' ( '.$fields.' ) VALUES ( '.$values.' )';
+}
+
+
+/**
+ * This private method is used to help construct
+ * the update/sql which is generated by GetInsertSQL and GetUpdateSQL.
+ * It handles the string construction of 1 column -> sql string based on
+ * the column type.  We want to do 'safe' handling of BLOBs
+ * 
+ * @param string the type of sql we are trying to create
+ *                'I' or 'U'. 
+ * @param string column data type from the db::MetaType() method  
+ * @param string the column name
+ * @param array the column value
+ * 
+ * @return string
+ * 
+ */
+function _adodb_column_sql_oci8(&$zthis,$action, $type, $fname, $fnameq, $arrFields, $magicq) 
+{
+    $sql = '';
+    
+    // Based on the datatype of the field
+    // Format the value properly for the database
+    switch($type) {
+    case 'B':
+        //in order to handle Blobs correctly, we need
+        //to do some magic for Oracle
+
+        //we need to create a new descriptor to handle 
+        //this properly
+        if (!empty($zthis->hasReturningInto)) {
+            if ($action == 'I') {
+                $sql = 'empty_blob(), ';
+            } else {
+                $sql = $fnameq. '=empty_blob(), ';
+            }
+            //add the variable to the returning clause array
+            //so the user can build this later in
+            //case they want to add more to it
+            $zthis->_returningArray[$fname] = ':xx'.$fname.'xx';
+        } else if (empty($arrFields[$fname])){
+            if ($action == 'I') {
+                $sql = 'empty_blob(), ';
+            } else {
+                $sql = $fnameq. '=empty_blob(), ';
+            }            
+        } else {
+            //this is to maintain compatibility
+            //with older adodb versions.
+            $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq, $arrFields, $magicq,false);
+        }
+        break;
+
+    case "X":
+        //we need to do some more magic here for long variables
+        //to handle these correctly in oracle.
+
+        //create a safe bind var name
+        //to avoid conflicts w/ dupes.
+       if (!empty($zthis->hasReturningInto)) {
+            if ($action == 'I') {
+                $sql = ':xx'.$fname.'xx, ';                
+            } else {
+                $sql = $fnameq.'=:xx'.$fname.'xx, ';
+            }
+            //add the variable to the returning clause array
+            //so the user can build this later in
+            //case they want to add more to it
+            $zthis->_returningArray[$fname] = ':xx'.$fname.'xx';
+        } else {
+            //this is to maintain compatibility
+            //with older adodb versions.
+            $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq, $arrFields, $magicq,false);
+        }            
+        break;
+        
+    default:
+        $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq,  $arrFields, $magicq,false);
+        break;
+    }
+    
+    return $sql;
+}    
+       
+function _adodb_column_sql(&$zthis, $action, $type, $fname, $fnameq, $arrFields, $magicq, $recurse=true) 
+{
+
+       if ($recurse) {
+               switch($zthis->dataProvider)  {
+               case 'postgres':
+                       if ($type == 'L') $type = 'C';
+                       break;
+               case 'oci8':
+                       return _adodb_column_sql_oci8($zthis, $action, $type, $fname, $fnameq, $arrFields, $magicq);
+                       
+               }
+       }
+               
+       switch($type) {
+               case "C":
+               case "X":
+               case 'B':
+                       $val = $zthis->qstr($arrFields[$fname],$magicq);
+                       break;
+
+               case "D":
+                       $val = $zthis->DBDate($arrFields[$fname]);
+                       break;
+
+               case "T":
+                       $val = $zthis->DBTimeStamp($arrFields[$fname]);
+                       break;
+
+               default:
+                       $val = $arrFields[$fname];
+                       if (empty($val)) $val = '0';
+                       break;
+       }
+
+       if ($action == 'I') return $val . ", ";
+       
+       
+       return $fnameq . "=" . $val  . ", ";
+       
+}
+
+
+
+function _adodb_debug_execute(&$zthis, $sql, $inputarr)
+{
+       $ss = '';
+       if ($inputarr) {
+               foreach($inputarr as $kk=>$vv) {
+                       if (is_string($vv) && strlen($vv)>64) $vv = substr($vv,0,64).'...';
+                       $ss .= "($kk=>'$vv') ";
+               }
+               $ss = "[ $ss ]";
+       }
+       $sqlTxt = is_array($sql) ? $sql[0] : $sql;
+       /*str_replace(', ','##1#__^LF',is_array($sql) ? $sql[0] : $sql);
+       $sqlTxt = str_replace(',',', ',$sqlTxt);
+       $sqlTxt = str_replace('##1#__^LF', ', ' ,$sqlTxt);
+       */
+       // check if running from browser or command-line
+       $inBrowser = isset($_SERVER['HTTP_USER_AGENT']);
+       
+       $dbt = $zthis->databaseType;
+       if (isset($zthis->dsnType)) $dbt .= '-'.$zthis->dsnType;
+       if ($inBrowser) {
+               $ss = htmlspecialchars($ss);
+               if ($zthis->debug === -1)
+                       ADOConnection::outp( "<br>\n($dbt): ".htmlspecialchars($sqlTxt)." &nbsp; <code>$ss</code>\n<br>\n",false);
+               else 
+                       ADOConnection::outp( "<hr>\n($dbt): ".htmlspecialchars($sqlTxt)." &nbsp; <code>$ss</code>\n<hr>\n",false);
+       } else {
+               ADOConnection::outp("-----\n($dbt): ".$sqlTxt."\n-----\n",false);
+       }
+
+       $qID = $zthis->_query($sql,$inputarr);
+       
+       /* 
+               Alexios Fakios notes that ErrorMsg() must be called before ErrorNo() for mssql
+               because ErrorNo() calls Execute('SELECT @ERROR'), causing recursion
+       */
+       if ($zthis->databaseType == 'mssql') { 
+       // ErrorNo is a slow function call in mssql, and not reliable in PHP 4.0.6
+               if($emsg = $zthis->ErrorMsg()) {
+                       if ($err = $zthis->ErrorNo()) ADOConnection::outp($err.': '.$emsg);
+               }
+       } else if (!$qID) {
+               ADOConnection::outp($zthis->ErrorNo() .': '. $zthis->ErrorMsg());
+       }
+       
+       if ($zthis->debug === 99) _adodb_backtrace(true,9999,2);
+       return $qID;
+}
+
+
+function _adodb_backtrace($printOrArr=true,$levels=9999,$skippy=0)
+{
+       if ((float) PHPVERSION() < 4.3) return '';
+        
+       $html =  (isset($_SERVER['HTTP_USER_AGENT']));
+       $fmt =  ($html) ? "</font><font color=#808080 size=-1> %% line %4d, file: <a href=\"file:/%s\">%s</a></font>" : "%% line %4d, file: %s";
+
+       $MAXSTRLEN = 128;
+
+       $s = ($html) ? '<pre align=left>' : '';
+       
+       if (is_array($printOrArr)) $traceArr = $printOrArr;
+       else $traceArr = debug_backtrace();
+       array_shift($traceArr);
+       array_shift($traceArr);
+       $tabs = sizeof($traceArr)-2;
+       
+       foreach ($traceArr as $arr) {
+               if ($skippy) {$skippy -= 1; continue;}
+               $levels -= 1;
+               if ($levels < 0) break;
+               
+               $args = array();
+               for ($i=0; $i < $tabs; $i++) $s .=  ($html) ? ' &nbsp; ' : "\t";
+               $tabs -= 1;
+               if ($html) $s .= '<font face="Courier New,Courier">';
+               if (isset($arr['class'])) $s .= $arr['class'].'.';
+               if (isset($arr['args']))
+                foreach($arr['args'] as $v) {
+                       if (is_null($v)) $args[] = 'null';
+                       else if (is_array($v)) $args[] = 'Array['.sizeof($v).']';
+                       else if (is_object($v)) $args[] = 'Object:'.get_class($v);
+                       else if (is_bool($v)) $args[] = $v ? 'true' : 'false';
+                       else {
+                               $v = (string) @$v;
+                               $str = htmlspecialchars(substr($v,0,$MAXSTRLEN));
+                               if (strlen($v) > $MAXSTRLEN) $str .= '...';
+                               $args[] = $str;
+                       }
+               }
+               $s .= $arr['function'].'('.implode(', ',$args).')';
+               
+               
+               $s .= @sprintf($fmt, $arr['line'],$arr['file'],basename($arr['file']));
+                       
+               $s .= "\n";
+       }       
+       if ($html) $s .= '</pre>';
+       if ($printOrArr) print $s;
+       
+       return $s;
+}
+
+?>
\ No newline at end of file
diff --git a/libraries/adodb/adodb-pager.inc.php b/libraries/adodb/adodb-pager.inc.php
new file mode 100644 (file)
index 0000000..5deb18e
--- /dev/null
@@ -0,0 +1,289 @@
+<?php\r
+\r
+/*\r
+       V4.65 22 July 2005  (c) 2000-2005 John Lim (jlim@natsoft.com.my). All rights reserved.\r
+         Released under both BSD license and Lesser GPL library license. \r
+         Whenever there is any discrepancy between the two licenses, \r
+         the BSD license will take precedence. \r
+         Set tabs to 4 for best viewing.\r
+\r
+       This class provides recordset pagination with \r
+       First/Prev/Next/Last links. \r
+       \r
+       Feel free to modify this class for your own use as\r
+       it is very basic. To learn how to use it, see the \r
+       example in adodb/tests/testpaging.php.\r
+       \r
+       "Pablo Costa" <pablo@cbsp.com.br> implemented Render_PageLinks().\r
+       \r
+       Please note, this class is entirely unsupported, \r
+       and no free support requests except for bug reports\r
+       will be entertained by the author.\r
+\r
+*/\r
+class ADODB_Pager {\r
+       var $id;        // unique id for pager (defaults to 'adodb')\r
+       var $db;        // ADODB connection object\r
+       var $sql;       // sql used\r
+       var $rs;        // recordset generated\r
+       var $curr_page; // current page number before Render() called, calculated in constructor\r
+       var $rows;              // number of rows per page\r
+    var $linksPerPage=10; // number of links per page in navigation bar\r
+    var $showPageLinks; \r
+\r
+       var $gridAttributes = 'width=100% border=1 bgcolor=white';\r
+       \r
+       // Localize text strings here\r
+       var $first = '<code>|&lt;</code>';\r
+       var $prev = '<code>&lt;&lt;</code>';\r
+       var $next = '<code>>></code>';\r
+       var $last = '<code>>|</code>';\r
+       var $moreLinks = '...';\r
+       var $startLinks = '...';\r
+       var $gridHeader = false;\r
+       var $htmlSpecialChars = true;\r
+       var $page = 'Page';\r
+       var $linkSelectedColor = 'red';\r
+       var $cache = 0;  #secs to cache with CachePageExecute()\r
+       \r
+       //----------------------------------------------\r
+       // constructor\r
+       //\r
+       // $db  adodb connection object\r
+       // $sql sql statement\r
+       // $id  optional id to identify which pager, \r
+       //              if you have multiple on 1 page. \r
+       //              $id should be only be [a-z0-9]*\r
+       //\r
+       function ADODB_Pager(&$db,$sql,$id = 'adodb', $showPageLinks = false)\r
+       {\r
+       global $PHP_SELF;\r
+       \r
+               $curr_page = $id.'_curr_page';\r
+               if (empty($PHP_SELF)) $PHP_SELF = $_SERVER['PHP_SELF'];\r
+               \r
+               $this->sql = $sql;\r
+               $this->id = $id;\r
+               $this->db = $db;\r
+               $this->showPageLinks = $showPageLinks;\r
+               \r
+               $next_page = $id.'_next_page';  \r
+               \r
+               if (isset($_GET[$next_page])) {\r
+                       $_SESSION[$curr_page] = $_GET[$next_page];\r
+               }\r
+               if (empty($_SESSION[$curr_page])) $_SESSION[$curr_page] = 1; ## at first page\r
+               \r
+               $this->curr_page = $_SESSION[$curr_page];\r
+               \r
+       }\r
+       \r
+       //---------------------------\r
+       // Display link to first page\r
+       function Render_First($anchor=true)\r
+       {\r
+       global $PHP_SELF;\r
+               if ($anchor) {\r
+       ?>\r
+               <a href="<?php echo $PHP_SELF,'?',$this->id;?>_next_page=1"><?php echo $this->first;?></a> &nbsp; \r
+       <?php\r
+               } else {\r
+                       print "$this->first &nbsp; ";\r
+               }\r
+       }\r
+       \r
+       //--------------------------\r
+       // Display link to next page\r
+       function render_next($anchor=true)\r
+       {\r
+       global $PHP_SELF;\r
+       \r
+               if ($anchor) {\r
+               ?>\r
+               <a href="<?php echo $PHP_SELF,'?',$this->id,'_next_page=',$this->rs->AbsolutePage() + 1 ?>"><?php echo $this->next;?></a> &nbsp; \r
+               <?php\r
+               } else {\r
+                       print "$this->next &nbsp; ";\r
+               }\r
+       }\r
+       \r
+       //------------------\r
+       // Link to last page\r
+       // \r
+       // for better performance with large recordsets, you can set\r
+       // $this->db->pageExecuteCountRows = false, which disables\r
+       // last page counting.\r
+       function render_last($anchor=true)\r
+       {\r
+       global $PHP_SELF;\r
+       \r
+               if (!$this->db->pageExecuteCountRows) return;\r
+               \r
+               if ($anchor) {\r
+               ?>\r
+                       <a href="<?php echo $PHP_SELF,'?',$this->id,'_next_page=',$this->rs->LastPageNo() ?>"><?php echo $this->last;?></a> &nbsp; \r
+               <?php\r
+               } else {\r
+                       print "$this->last &nbsp; ";\r
+               }\r
+       }\r
+       \r
+       //---------------------------------------------------\r
+       // original code by "Pablo Costa" <pablo@cbsp.com.br> \r
+        function render_pagelinks()\r
+        {\r
+        global $PHP_SELF;\r
+            $pages        = $this->rs->LastPageNo();\r
+            $linksperpage = $this->linksPerPage ? $this->linksPerPage : $pages;\r
+            for($i=1; $i <= $pages; $i+=$linksperpage)\r
+            {\r
+                if($this->rs->AbsolutePage() >= $i)\r
+                {\r
+                    $start = $i;\r
+                }\r
+            }\r
+                       $numbers = '';\r
+            $end = $start+$linksperpage-1;\r
+                       $link = $this->id . "_next_page";\r
+            if($end > $pages) $end = $pages;\r
+                       \r
+                       \r
+                       if ($this->startLinks && $start > 1) {\r
+                               $pos = $start - 1;\r
+                               $numbers .= "<a href=$PHP_SELF?$link=$pos>$this->startLinks</a>  ";\r
+            } \r
+                       \r
+                       for($i=$start; $i <= $end; $i++) {\r
+                if ($this->rs->AbsolutePage() == $i)\r
+                    $numbers .= "<font color=$this->linkSelectedColor><b>$i</b></font>  ";\r
+                else \r
+                     $numbers .= "<a href=$PHP_SELF?$link=$i>$i</a>  ";\r
+            \r
+            }\r
+                       if ($this->moreLinks && $end < $pages) \r
+                               $numbers .= "<a href=$PHP_SELF?$link=$i>$this->moreLinks</a>  ";\r
+            print $numbers . ' &nbsp; ';\r
+        }\r
+       // Link to previous page\r
+       function render_prev($anchor=true)\r
+       {\r
+       global $PHP_SELF;\r
+               if ($anchor) {\r
+       ?>\r
+               <a href="<?php echo $PHP_SELF,'?',$this->id,'_next_page=',$this->rs->AbsolutePage() - 1 ?>"><?php echo $this->prev;?></a> &nbsp; \r
+       <?php \r
+               } else {\r
+                       print "$this->prev &nbsp; ";\r
+               }\r
+       }\r
+       \r
+       //--------------------------------------------------------\r
+       // Simply rendering of grid. You should override this for\r
+       // better control over the format of the grid\r
+       //\r
+       // We use output buffering to keep code clean and readable.\r
+       function RenderGrid()\r
+       {\r
+       global $gSQLBlockRows; // used by rs2html to indicate how many rows to display\r
+               include_once(ADODB_DIR.'/tohtml.inc.php');\r
+               ob_start();\r
+               $gSQLBlockRows = $this->rows;\r
+               rs2html($this->rs,$this->gridAttributes,$this->gridHeader,$this->htmlSpecialChars);\r
+               $s = ob_get_contents();\r
+               ob_end_clean();\r
+               return $s;\r
+       }\r
+       \r
+       //-------------------------------------------------------\r
+       // Navigation bar\r
+       //\r
+       // we use output buffering to keep the code easy to read.\r
+       function RenderNav()\r
+       {\r
+               ob_start();\r
+               if (!$this->rs->AtFirstPage()) {\r
+                       $this->Render_First();\r
+                       $this->Render_Prev();\r
+               } else {\r
+                       $this->Render_First(false);\r
+                       $this->Render_Prev(false);\r
+               }\r
+        if ($this->showPageLinks){\r
+            $this->Render_PageLinks();\r
+        }\r
+               if (!$this->rs->AtLastPage()) {\r
+                       $this->Render_Next();\r
+                       $this->Render_Last();\r
+               } else {\r
+                       $this->Render_Next(false);\r
+                       $this->Render_Last(false);\r
+               }\r
+               $s = ob_get_contents();\r
+               ob_end_clean();\r
+               return $s;\r
+       }\r
+       \r
+       //-------------------\r
+       // This is the footer\r
+       function RenderPageCount()\r
+       {\r
+               if (!$this->db->pageExecuteCountRows) return '';\r
+               $lastPage = $this->rs->LastPageNo();\r
+               if ($lastPage == -1) $lastPage = 1; // check for empty rs.\r
+               if ($this->curr_page > $lastPage) $this->curr_page = 1;\r
+               return "<font size=-1>$this->page ".$this->curr_page."/".$lastPage."</font>";\r
+       }\r
+       \r
+       //-----------------------------------\r
+       // Call this class to draw everything.\r
+       function Render($rows=10)\r
+       {\r
+       global $ADODB_COUNTRECS;\r
+       \r
+               $this->rows = $rows;\r
+               \r
+               if ($this->db->dataProvider == 'informix') $this->db->cursorType = IFX_SCROLL;\r
+               \r
+               $savec = $ADODB_COUNTRECS;\r
+               if ($this->db->pageExecuteCountRows) $ADODB_COUNTRECS = true;\r
+               if ($this->cache)\r
+                       $rs = &$this->db->CachePageExecute($this->cache,$this->sql,$rows,$this->curr_page);\r
+               else\r
+                       $rs = &$this->db->PageExecute($this->sql,$rows,$this->curr_page);\r
+               $ADODB_COUNTRECS = $savec;\r
+               \r
+               $this->rs = &$rs;\r
+               if (!$rs) {\r
+                       print "<h3>Query failed: $this->sql</h3>";\r
+                       return;\r
+               }\r
+               \r
+               if (!$rs->EOF && (!$rs->AtFirstPage() || !$rs->AtLastPage())) \r
+                       $header = $this->RenderNav();\r
+               else\r
+                       $header = "&nbsp;";\r
+               \r
+               $grid = $this->RenderGrid();\r
+               $footer = $this->RenderPageCount();\r
+               $rs->Close();\r
+               $this->rs = false;\r
+               \r
+               $this->RenderLayout($header,$grid,$footer);\r
+       }\r
+       \r
+       //------------------------------------------------------\r
+       // override this to control overall layout and formating\r
+       function RenderLayout($header,$grid,$footer,$attributes='border=1 bgcolor=beige')\r
+       {\r
+               echo "<table ".$attributes."><tr><td>",\r
+                               $header,\r
+                       "</td></tr><tr><td>",\r
+                               $grid,\r
+                       "</td></tr><tr><td>",\r
+                               $footer,\r
+                       "</td></tr></table>";\r
+       }\r
+}\r
+\r
+\r
+?>
\ No newline at end of file
diff --git a/libraries/adodb/adodb-pear.inc.php b/libraries/adodb/adodb-pear.inc.php
new file mode 100755 (executable)
index 0000000..2ed5213
--- /dev/null
@@ -0,0 +1,374 @@
+<?php\r
+/** \r
+ * @version V4.65 22 July 2005 (c) 2000-2005 John Lim (jlim@natsoft.com.my). All rights reserved.\r
+ * Released under both BSD license and Lesser GPL library license. \r
+ * Whenever there is any discrepancy between the two licenses, \r
+ * the BSD license will take precedence. \r
+ *\r
+ * Set tabs to 4 for best viewing.\r
+ * \r
+ * PEAR DB Emulation Layer for ADODB.\r
+ *\r
+ * The following code is modelled on PEAR DB code by Stig Bakken <ssb@fast.no>                                                            |\r
+ * and Tomas V.V.Cox <cox@idecnet.com>.        Portions (c)1997-2002 The PHP Group.\r
+ */\r
+\r
+ /*\r
+ We support:\r
\r
+ DB_Common\r
+ ---------\r
+       query - returns PEAR_Error on error\r
+       limitQuery - return PEAR_Error on error\r
+       prepare - does not return PEAR_Error on error\r
+       execute - does not return PEAR_Error on error\r
+       setFetchMode - supports ASSOC and ORDERED\r
+       errorNative\r
+       quote\r
+       nextID\r
+       disconnect\r
+       \r
+       getOne\r
+       getAssoc\r
+       getRow\r
+       getCol\r
+       getAll\r
+       \r
+ DB_Result\r
+ ---------\r
+       numRows - returns -1 if not supported\r
+       numCols\r
+       fetchInto - does not support passing of fetchmode\r
+       fetchRows - does not support passing of fetchmode\r
+       free\r
+ */\r
\r
+define('ADODB_PEAR',dirname(__FILE__));\r
+include_once "PEAR.php";\r
+include_once ADODB_PEAR."/adodb-errorpear.inc.php";\r
+include_once ADODB_PEAR."/adodb.inc.php";\r
+\r
+if (!defined('DB_OK')) {\r
+define("DB_OK",        1);\r
+define("DB_ERROR",-1);\r
+\r
+// autoExecute constants\r
+define('DB_AUTOQUERY_INSERT', 1);\r
+define('DB_AUTOQUERY_UPDATE', 2);\r
+\r
+/**\r
+ * This is a special constant that tells DB the user hasn't specified\r
+ * any particular get mode, so the default should be used.\r
+ */\r
+\r
+define('DB_FETCHMODE_DEFAULT', 0);\r
+\r
+/**\r
+ * Column data indexed by numbers, ordered from 0 and up\r
+ */\r
+\r
+define('DB_FETCHMODE_ORDERED', 1);\r
+\r
+/**\r
+ * Column data indexed by column names\r
+ */\r
+\r
+define('DB_FETCHMODE_ASSOC', 2);\r
+\r
+/* for compatibility */\r
+\r
+define('DB_GETMODE_ORDERED', DB_FETCHMODE_ORDERED);\r
+define('DB_GETMODE_ASSOC',   DB_FETCHMODE_ASSOC);\r
+\r
+/**\r
+ * these are constants for the tableInfo-function\r
+ * they are bitwised or'ed. so if there are more constants to be defined\r
+ * in the future, adjust DB_TABLEINFO_FULL accordingly\r
+ */\r
+\r
+define('DB_TABLEINFO_ORDER', 1);\r
+define('DB_TABLEINFO_ORDERTABLE', 2);\r
+define('DB_TABLEINFO_FULL', 3);\r
+}\r
+\r
+/**\r
+ * The main "DB" class is simply a container class with some static\r
+ * methods for creating DB objects as well as some utility functions\r
+ * common to all parts of DB.\r
+ *\r
+ */\r
+\r
+class DB\r
+{\r
+       /**\r
+        * Create a new DB object for the specified database type\r
+        *\r
+        * @param $type string database type, for example "mysql"\r
+        *\r
+        * @return object a newly created DB object, or a DB error code on\r
+        * error\r
+        */\r
+\r
+       function &factory($type)\r
+       {\r
+               include_once(ADODB_DIR."/drivers/adodb-$type.inc.php");\r
+               $obj = &NewADOConnection($type);\r
+               if (!is_object($obj)) $obj =& new PEAR_Error('Unknown Database Driver: '.$dsninfo['phptype'],-1);\r
+               return $obj;\r
+       }\r
+\r
+       /**\r
+        * Create a new DB object and connect to the specified database\r
+        *\r
+        * @param $dsn mixed "data source name", see the DB::parseDSN\r
+        * method for a description of the dsn format.  Can also be\r
+        * specified as an array of the format returned by DB::parseDSN.\r
+        *\r
+        * @param $options mixed if boolean (or scalar), tells whether\r
+        * this connection should be persistent (for backends that support\r
+        * this).  This parameter can also be an array of options, see\r
+        * DB_common::setOption for more information on connection\r
+        * options.\r
+        *\r
+        * @return object a newly created DB connection object, or a DB\r
+        * error object on error\r
+        *\r
+        * @see DB::parseDSN\r
+        * @see DB::isError\r
+        */\r
+       function &connect($dsn, $options = false)\r
+       {\r
+               if (is_array($dsn)) {\r
+                       $dsninfo = $dsn;\r
+               } else {\r
+                       $dsninfo = DB::parseDSN($dsn);\r
+               }\r
+               switch ($dsninfo["phptype"]) {\r
+                       case 'pgsql':   $type = 'postgres7'; break;\r
+                       case 'ifx':             $type = 'informix9'; break;\r
+                       default:                $type = $dsninfo["phptype"]; break;\r
+               }\r
+\r
+               if (is_array($options) && isset($options["debug"]) &&\r
+                       $options["debug"] >= 2) {\r
+                       // expose php errors with sufficient debug level\r
+                        @include_once("adodb-$type.inc.php");\r
+               } else {\r
+                        @include_once("adodb-$type.inc.php");\r
+               }\r
+\r
+               @$obj =& NewADOConnection($type);\r
+               if (!is_object($obj)) {\r
+                       $obj =& new PEAR_Error('Unknown Database Driver: '.$dsninfo['phptype'],-1);\r
+                       return $obj;\r
+               }\r
+               if (is_array($options)) {\r
+                       foreach($options as $k => $v) {\r
+                               switch(strtolower($k)) {\r
+                               case 'persist':\r
+                               case 'persistent':      $persist = $v; break;\r
+                               #ibase\r
+                               case 'dialect':         $obj->dialect = $v; break;\r
+                               case 'charset':         $obj->charset = $v; break;\r
+                               case 'buffers':         $obj->buffers = $v; break;\r
+                               #ado\r
+                               case 'charpage':        $obj->charPage = $v; break;\r
+                               #mysql\r
+                               case 'clientflags': $obj->clientFlags = $v; break;\r
+                               }\r
+                       }\r
+               } else {\r
+                       $persist = false;\r
+               }\r
+\r
+               if (isset($dsninfo['socket'])) $dsninfo['hostspec'] .= ':'.$dsninfo['socket'];\r
+               else if (isset($dsninfo['port'])) $dsninfo['hostspec'] .= ':'.$dsninfo['port'];\r
+               \r
+               if($persist) $ok = $obj->PConnect($dsninfo['hostspec'], $dsninfo['username'],$dsninfo['password'],$dsninfo['database']);\r
+               else  $ok = $obj->Connect($dsninfo['hostspec'], $dsninfo['username'],$dsninfo['password'],$dsninfo['database']);\r
+               \r
+               if (!$ok) $obj = ADODB_PEAR_Error();\r
+               return $obj;\r
+       }\r
+\r
+       /**\r
+        * Return the DB API version\r
+        *\r
+        * @return int the DB API version number\r
+        */\r
+       function apiVersion()\r
+       {\r
+               return 2;\r
+       }\r
+\r
+       /**\r
+        * Tell whether a result code from a DB method is an error\r
+        *\r
+        * @param $value int result code\r
+        *\r
+        * @return bool whether $value is an error\r
+        */\r
+       function isError($value)\r
+       {\r
+               if (!is_object($value)) return false;\r
+               $class = get_class($value);\r
+               return $class == 'pear_error' || is_subclass_of($value, 'pear_error') || \r
+                               $class == 'db_error' || is_subclass_of($value, 'db_error');\r
+       }\r
+\r
+\r
+       /**\r
+        * Tell whether a result code from a DB method is a warning.\r
+        * Warnings differ from errors in that they are generated by DB,\r
+        * and are not fatal.\r
+        *\r
+        * @param $value mixed result value\r
+        *\r
+        * @return bool whether $value is a warning\r
+        */\r
+       function isWarning($value)\r
+       {\r
+               return false;\r
+               /*\r
+               return is_object($value) &&\r
+                       (get_class( $value ) == "db_warning" ||\r
+                        is_subclass_of($value, "db_warning"));*/\r
+       }\r
+\r
+       /**\r
+        * Parse a data source name\r
+        *\r
+        * @param $dsn string Data Source Name to be parsed\r
+        *\r
+        * @return array an associative array with the following keys:\r
+        *\r
+        *  phptype: Database backend used in PHP (mysql, odbc etc.)\r
+        *  dbsyntax: Database used with regards to SQL syntax etc.\r
+        *  protocol: Communication protocol to use (tcp, unix etc.)\r
+        *  hostspec: Host specification (hostname[:port])\r
+        *  database: Database to use on the DBMS server\r
+        *  username: User name for login\r
+        *  password: Password for login\r
+        *\r
+        * The format of the supplied DSN is in its fullest form:\r
+        *\r
+        *  phptype(dbsyntax)://username:password@protocol+hostspec/database\r
+        *\r
+        * Most variations are allowed:\r
+        *\r
+        *  phptype://username:password@protocol+hostspec:110//usr/db_file.db\r
+        *  phptype://username:password@hostspec/database_name\r
+        *  phptype://username:password@hostspec\r
+        *  phptype://username@hostspec\r
+        *  phptype://hostspec/database\r
+        *  phptype://hostspec\r
+        *  phptype(dbsyntax)\r
+        *  phptype\r
+        *\r
+        * @author Tomas V.V.Cox <cox@idecnet.com>\r
+        */\r
+       function parseDSN($dsn)\r
+       {\r
+               if (is_array($dsn)) {\r
+                       return $dsn;\r
+               }\r
+\r
+               $parsed = array(\r
+                       'phptype'  => false,\r
+                       'dbsyntax' => false,\r
+                       'protocol' => false,\r
+                       'hostspec' => false,\r
+                       'database' => false,\r
+                       'username' => false,\r
+                       'password' => false\r
+               );\r
+\r
+               // Find phptype and dbsyntax\r
+               if (($pos = strpos($dsn, '://')) !== false) {\r
+                       $str = substr($dsn, 0, $pos);\r
+                       $dsn = substr($dsn, $pos + 3);\r
+               } else {\r
+                       $str = $dsn;\r
+                       $dsn = NULL;\r
+               }\r
+\r
+               // Get phptype and dbsyntax\r
+               // $str => phptype(dbsyntax)\r
+               if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) {\r
+                       $parsed['phptype'] = $arr[1];\r
+                       $parsed['dbsyntax'] = (empty($arr[2])) ? $arr[1] : $arr[2];\r
+               } else {\r
+                       $parsed['phptype'] = $str;\r
+                       $parsed['dbsyntax'] = $str;\r
+               }\r
+\r
+               if (empty($dsn)) {\r
+                       return $parsed;\r
+               }\r
+\r
+               // Get (if found): username and password\r
+               // $dsn => username:password@protocol+hostspec/database\r
+               if (($at = strpos($dsn,'@')) !== false) {\r
+                       $str = substr($dsn, 0, $at);\r
+                       $dsn = substr($dsn, $at + 1);\r
+                       if (($pos = strpos($str, ':')) !== false) {\r
+                               $parsed['username'] = urldecode(substr($str, 0, $pos));\r
+                               $parsed['password'] = urldecode(substr($str, $pos + 1));\r
+                       } else {\r
+                               $parsed['username'] = urldecode($str);\r
+                       }\r
+               }\r
+\r
+               // Find protocol and hostspec\r
+               // $dsn => protocol+hostspec/database\r
+               if (($pos=strpos($dsn, '/')) !== false) {\r
+                       $str = substr($dsn, 0, $pos);\r
+                       $dsn = substr($dsn, $pos + 1);\r
+               } else {\r
+                       $str = $dsn;\r
+                       $dsn = NULL;\r
+               }\r
+\r
+               // Get protocol + hostspec\r
+               // $str => protocol+hostspec\r
+               if (($pos=strpos($str, '+')) !== false) {\r
+                       $parsed['protocol'] = substr($str, 0, $pos);\r
+                       $parsed['hostspec'] = urldecode(substr($str, $pos + 1));\r
+               } else {\r
+                       $parsed['hostspec'] = urldecode($str);\r
+               }\r
+\r
+               // Get dabase if any\r
+               // $dsn => database\r
+               if (!empty($dsn)) {\r
+                       $parsed['database'] = $dsn;\r
+               }\r
+\r
+               return $parsed;\r
+       }\r
+\r
+       /**\r
+        * Load a PHP database extension if it is not loaded already.\r
+        *\r
+        * @access public\r
+        *\r
+        * @param $name the base name of the extension (without the .so or\r
+        * .dll suffix)\r
+        *\r
+        * @return bool true if the extension was already or successfully\r
+        * loaded, false if it could not be loaded\r
+        */\r
+       function assertExtension($name)\r
+       {\r
+               if (!extension_loaded($name)) {\r
+                       $dlext = (strncmp(PHP_OS,'WIN',3) === 0) ? '.dll' : '.so';\r
+                       @dl($name . $dlext);\r
+               }\r
+               if (!extension_loaded($name)) {\r
+                       return false;\r
+               }\r
+               return true;\r
+       }\r
+}\r
+\r
+?>
\ No newline at end of file
diff --git a/libraries/adodb/adodb-perf.inc.php b/libraries/adodb/adodb-perf.inc.php
new file mode 100755 (executable)
index 0000000..3a1fa4d
--- /dev/null
@@ -0,0 +1,1053 @@
+<?php\r
+/* \r
+V4.65 22 July 2005  (c) 2000-2005 John Lim (jlim@natsoft.com.my). All rights reserved.\r
+  Released under both BSD license and Lesser GPL library license. \r
+  Whenever there is any discrepancy between the two licenses, \r
+  the BSD license will take precedence. See License.txt. \r
+  Set tabs to 4 for best viewing.\r
+  \r
+  Latest version is available at http://adodb.sourceforge.net\r
+  \r
+  Library for basic performance monitoring and tuning.\r
+  \r
+  My apologies if you see code mixed with presentation. The presentation suits\r
+  my needs. If you want to separate code from presentation, be my guest. Patches\r
+  are welcome.\r
+  \r
+*/\r
+\r
+if (!defined(ADODB_DIR)) include_once(dirname(__FILE__).'/adodb.inc.php');\r
+include_once(ADODB_DIR.'/tohtml.inc.php');\r
+\r
+define( 'ADODB_OPT_HIGH', 2);\r
+define( 'ADODB_OPT_LOW', 1);\r
+\r
+// returns in K the memory of current process, or 0 if not known\r
+function adodb_getmem()\r
+{\r
+       if (function_exists('memory_get_usage'))\r
+               return (integer) ((memory_get_usage()+512)/1024);\r
+       \r
+       $pid = getmypid();\r
+       \r
+       if ( strncmp(strtoupper(PHP_OS),'WIN',3)==0) {\r
+               $output = array();\r
+       \r
+               exec('tasklist /FI "PID eq ' . $pid. '" /FO LIST', $output); \r
+               return substr($output[5], strpos($output[5], ':') + 1);\r
+       } \r
+       \r
+       /* Hopefully UNIX */\r
+       exec("ps --pid $pid --no-headers -o%mem,size", $output);\r
+       if (sizeof($output) == 0) return 0;\r
+       \r
+       $memarr = explode(' ',$output[0]);\r
+       if (sizeof($memarr)>=2) return (integer) $memarr[1];\r
+       \r
+       return 0;\r
+}\r
+\r
+// avoids localization problems where , is used instead of .\r
+function adodb_round($n,$prec)\r
+{\r
+       return number_format($n, $prec, '.', '');\r
+}\r
+\r
+/* return microtime value as a float */\r
+function adodb_microtime()\r
+{\r
+       $t = microtime();\r
+       $t = explode(' ',$t);\r
+       return (float)$t[1]+ (float)$t[0];\r
+}\r
+\r
+/* sql code timing */\r
+function& adodb_log_sql(&$conn,$sql,$inputarr)\r
+{\r
+       \r
+    $perf_table = adodb_perf::table();\r
+       $conn->fnExecute = false;\r
+       $t0 = microtime();\r
+       $rs =& $conn->Execute($sql,$inputarr);\r
+       $t1 = microtime();\r
+\r
+       if (!empty($conn->_logsql)) {\r
+               $conn->_logsql = false; // disable logsql error simulation\r
+               $dbT = $conn->databaseType;\r
+               \r
+               $a0 = split(' ',$t0);\r
+               $a0 = (float)$a0[1]+(float)$a0[0];\r
+               \r
+               $a1 = split(' ',$t1);\r
+               $a1 = (float)$a1[1]+(float)$a1[0];\r
+               \r
+               $time = $a1 - $a0;\r
+       \r
+               if (!$rs) {\r
+                       $errM = $conn->ErrorMsg();\r
+                       $errN = $conn->ErrorNo();\r
+                       $conn->lastInsID = 0;\r
+                       $tracer = substr('ERROR: '.htmlspecialchars($errM),0,250);\r
+               } else {\r
+                       $tracer = '';\r
+                       $errM = '';\r
+                       $errN = 0;\r
+                       $dbg = $conn->debug;\r
+                       $conn->debug = false;\r
+                       if (!is_object($rs) || $rs->dataProvider == 'empty') \r
+                               $conn->_affected = $conn->affected_rows(true);\r
+                       $conn->lastInsID = @$conn->Insert_ID();\r
+                       $conn->debug = $dbg;\r
+               }\r
+               if (isset($_SERVER['HTTP_HOST'])) {\r
+                       $tracer .= '<br>'.$_SERVER['HTTP_HOST'];\r
+                       if (isset($_SERVER['PHP_SELF'])) $tracer .= $_SERVER['PHP_SELF'];\r
+               } else \r
+                       if (isset($_SERVER['PHP_SELF'])) $tracer .= '<br>'.$_SERVER['PHP_SELF'];\r
+               //$tracer .= (string) adodb_backtrace(false);\r
+               \r
+               $tracer = (string) substr($tracer,0,500);\r
+               \r
+               if (is_array($inputarr)) {\r
+                       if (is_array(reset($inputarr))) $params = 'Array sizeof='.sizeof($inputarr);\r
+                       else {\r
+                               // Quote string parameters so we can see them in the\r
+                               // performance stats. This helps spot disabled indexes.\r
+                               $xar_params = $inputarr;\r
+                               foreach ($xar_params as $xar_param_key => $xar_param) {\r
+                                       if (gettype($xar_param) == 'string')\r
+                                       $xar_params[$xar_param_key] = '"' . $xar_param . '"';\r
+                               }\r
+                               $params = implode(', ', $xar_params);\r
+                               if (strlen($params) >= 3000) $params = substr($params, 0, 3000);\r
+                       }\r
+               } else {\r
+                       $params = '';\r
+               }\r
+               \r
+               if (is_array($sql)) $sql = $sql[0];\r
+               $arr = array('b'=>strlen($sql).'.'.crc32($sql),\r
+                                       'c'=>substr($sql,0,3900), 'd'=>$params,'e'=>$tracer,'f'=>adodb_round($time,6));\r
+               //var_dump($arr);\r
+               $saved = $conn->debug;\r
+               $conn->debug = 0;\r
+               \r
+               $d = $conn->sysTimeStamp;\r
+               if (empty($d)) $d = date("'Y-m-d H:i:s'");\r
+               if ($conn->dataProvider == 'oci8' && $dbT != 'oci8po') {\r
+                       $isql = "insert into $perf_table values($d,:b,:c,:d,:e,:f)";\r
+               } else if ($dbT == 'odbc_mssql' || $dbT == 'informix') {\r
+                       $timer = $arr['f'];\r
+                       if ($dbT == 'informix') $sql2 = substr($sql2,0,230);\r
+\r
+                       $sql1 = $conn->qstr($arr['b']);\r
+                       $sql2 = $conn->qstr($arr['c']);\r
+                       $params = $conn->qstr($arr['d']);\r
+                       $tracer = $conn->qstr($arr['e']);\r
+                       \r
+                       $isql = "insert into $perf_table (created,sql0,sql1,params,tracer,timer) values($d,$sql1,$sql2,$params,$tracer,$timer)";\r
+                       if ($dbT == 'informix') $isql = str_replace(chr(10),' ',$isql);\r
+                       $arr = false;\r
+               } else {\r
+                       $isql = "insert into $perf_table (created,sql0,sql1,params,tracer,timer) values( $d,?,?,?,?,?)";\r
+               }\r
+\r
+               $ok = $conn->Execute($isql,$arr);\r
+               $conn->debug = $saved;\r
+               \r
+               if ($ok) {\r
+                       $conn->_logsql = true; \r
+               } else {\r
+                       $err2 = $conn->ErrorMsg();\r
+                       $conn->_logsql = true; // enable logsql error simulation\r
+                       $perf =& NewPerfMonitor($conn);\r
+                       if ($perf) {\r
+                               if ($perf->CreateLogTable()) $ok = $conn->Execute($isql,$arr);\r
+                       } else {\r
+                               $ok = $conn->Execute("create table $perf_table (\r
+                               created varchar(50),\r
+                               sql0 varchar(250), \r
+                               sql1 varchar(4000),\r
+                               params varchar(3000),\r
+                               tracer varchar(500),\r
+                               timer decimal(16,6))");\r
+                       }\r
+                       if (!$ok) {\r
+                               ADOConnection::outp( "<p><b>LOGSQL Insert Failed</b>: $isql<br>$err2</p>");\r
+                               $conn->_logsql = false;\r
+                       }\r
+               }\r
+               $conn->_errorMsg = $errM;\r
+               $conn->_errorCode = $errN;\r
+       } \r
+       $conn->fnExecute = 'adodb_log_sql';\r
+       return $rs;\r
+}\r
+\r
+       \r
+/*\r
+The settings data structure is an associative array that database parameter per element.\r
+\r
+Each database parameter element in the array is itself an array consisting of:\r
+\r
+0: category code, used to group related db parameters\r
+1: either\r
+       a. sql string to retrieve value, eg. "select value from v\$parameter where name='db_block_size'", \r
+       b. array holding sql string and field to look for, e.g. array('show variables','table_cache'),\r
+       c. a string prefixed by =, then a PHP method of the class is invoked, \r
+               e.g. to invoke $this->GetIndexValue(), set this array element to '=GetIndexValue',\r
+2: description of the database parameter\r
+*/\r
+\r
+class adodb_perf {\r
+       var $conn;\r
+       var $color = '#F0F0F0';\r
+       var $table = '<table border=1 bgcolor=white>';\r
+       var $titles = '<tr><td><b>Parameter</b></td><td><b>Value</b></td><td><b>Description</b></td></tr>';\r
+       var $warnRatio = 90;\r
+       var $tablesSQL = false;\r
+       var $cliFormat = "%32s => %s \r\n";\r
+       var $sql1 = 'sql1';  // used for casting sql1 to text for mssql\r
+       var $explain = true;\r
+       var $helpurl = "<a href=http://phplens.com/adodb/reference.functions.fnexecute.and.fncacheexecute.properties.html#logsql>LogSQL help</a>";\r
+       var $createTableSQL = false;\r
+       var $maxLength = 2000;\r
+       \r
+    // Sets the tablename to be used            \r
+    function table($newtable = false)\r
+    {\r
+        static $_table;\r
+\r
+        if (!empty($newtable))  $_table = $newtable;\r
+               if (empty($_table)) $_table = 'adodb_logsql';\r
+        return $_table;\r
+    }\r
+\r
+       // returns array with info to calculate CPU Load\r
+       function _CPULoad()\r
+       {\r
+/*\r
+\r
+cpu  524152 2662 2515228 336057010\r
+cpu0 264339 1408 1257951 168025827\r
+cpu1 259813 1254 1257277 168031181\r
+page 622307 25475680\r
+swap 24 1891\r
+intr 890153570 868093576 6 0 4 4 0 6 1 2 0 0 0 124 0 8098760 2 13961053 0 0 0 0 0 0 0 0 0 0 0 0 0 16 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\r
+disk_io: (3,0):(3144904,54369,610378,3090535,50936192) (3,1):(3630212,54097,633016,3576115,50951320)\r
+ctxt 66155838\r
+btime 1062315585\r
+processes 69293\r
+\r
+*/\r
+               // Algorithm is taken from\r
+               // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/example__obtaining_raw_performance_data.asp\r
+               if (strncmp(PHP_OS,'WIN',3)==0) {\r
+                       if (PHP_VERSION == '5.0.0') return false;\r
+                       if (PHP_VERSION == '5.0.1') return false;\r
+                       if (PHP_VERSION == '5.0.2') return false;\r
+                       if (PHP_VERSION == '5.0.3') return false;\r
+                       if (PHP_VERSION == '4.3.10') return false; # see http://bugs.php.net/bug.php?id=31737\r
+                       \r
+                       @$c = new COM("WinMgmts:{impersonationLevel=impersonate}!Win32_PerfRawData_PerfOS_Processor.Name='_Total'");\r
+                       if (!$c) return false;\r
+                       \r
+                       $info[0] = $c->PercentProcessorTime;\r
+                       $info[1] = 0;\r
+                       $info[2] = 0;\r
+                       $info[3] = $c->TimeStamp_Sys100NS;\r
+                       //print_r($info);\r
+                       return $info;\r
+               }\r
+               \r
+               // Algorithm - Steve Blinch (BlitzAffe Online, http://www.blitzaffe.com)\r
+               $statfile = '/proc/stat';\r
+               if (!file_exists($statfile)) return false;\r
+               \r
+               $fd = fopen($statfile,"r");\r
+               if (!$fd) return false;\r
+               \r
+               $statinfo = explode("\n",fgets($fd, 1024));\r
+               fclose($fd);\r
+               foreach($statinfo as $line) {\r
+                       $info = explode(" ",$line);\r
+                       if($info[0]=="cpu") {\r
+                               array_shift($info);  // pop off "cpu"\r
+                               if(!$info[0]) array_shift($info); // pop off blank space (if any)\r
+                               return $info;\r
+                       }\r
+               }\r
+               \r
+               return false;\r
+               \r
+       }\r
+       \r
+       /* NOT IMPLEMENTED */\r
+       function MemInfo()\r
+       {\r
+               /*\r
+\r
+        total:    used:    free:  shared: buffers:  cached:\r
+Mem:  1055289344 917299200 137990144        0 165437440 599773184\r
+Swap: 2146775040 11055104 2135719936\r
+MemTotal:      1030556 kB\r
+MemFree:        134756 kB\r
+MemShared:           0 kB\r
+Buffers:        161560 kB\r
+Cached:         581384 kB\r
+SwapCached:       4332 kB\r
+Active:         494468 kB\r
+Inact_dirty:    322856 kB\r
+Inact_clean:     24256 kB\r
+Inact_target:   168316 kB\r
+HighTotal:      131064 kB\r
+HighFree:         1024 kB\r
+LowTotal:       899492 kB\r
+LowFree:        133732 kB\r
+SwapTotal:     2096460 kB\r
+SwapFree:      2085664 kB\r
+Committed_AS:   348732 kB\r
+               */\r
+       }\r
+       \r
+       \r
+       /*\r
+               Remember that this is client load, not db server load!\r
+       */\r
+       var $_lastLoad;\r
+       function CPULoad()\r
+       {\r
+               $info = $this->_CPULoad();\r
+               if (!$info) return false;\r
+                       \r
+               if (empty($this->_lastLoad)) {\r
+                       sleep(1);\r
+                       $this->_lastLoad = $info;\r
+                       $info = $this->_CPULoad();\r
+               }\r
+               \r
+               $last = $this->_lastLoad;\r
+               $this->_lastLoad = $info;\r
+               \r
+               $d_user = $info[0] - $last[0];\r
+               $d_nice = $info[1] - $last[1];\r
+               $d_system = $info[2] - $last[2];\r
+               $d_idle = $info[3] - $last[3];\r
+               \r
+               //printf("Delta - User: %f  Nice: %f  System: %f  Idle: %f<br>",$d_user,$d_nice,$d_system,$d_idle);\r
+\r
+               if (strncmp(PHP_OS,'WIN',3)==0) {\r
+                       if ($d_idle < 1) $d_idle = 1;\r
+                       return 100*(1-$d_user/$d_idle);\r
+               }else {\r
+                       $total=$d_user+$d_nice+$d_system+$d_idle;\r
+                       if ($total<1) $total=1;\r
+                       return 100*($d_user+$d_nice+$d_system)/$total; \r
+               }\r
+       }\r
+       \r
+       function Tracer($sql)\r
+       {\r
+        $perf_table = adodb_perf::table();\r
+               $saveE = $this->conn->fnExecute;\r
+               $this->conn->fnExecute = false;\r
+               \r
+               global $ADODB_FETCH_MODE;\r
+               $save = $ADODB_FETCH_MODE;\r
+               $ADODB_FETCH_MODE = ADODB_FETCH_NUM;\r
+               if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);\r
+                               \r
+               $sqlq = $this->conn->qstr($sql);\r
+               $arr = $this->conn->GetArray(\r
+"select count(*),tracer \r
+       from $perf_table where sql1=$sqlq \r
+       group by tracer\r
+       order by 1 desc");\r
+               $s = '';\r
+               if ($arr) {\r
+                       $s .= '<h3>Scripts Affected</h3>';\r
+                       foreach($arr as $k) {\r
+                               $s .= sprintf("%4d",$k[0]).' &nbsp; '.strip_tags($k[1]).'<br>';\r
+                       }\r
+               }\r
+               \r
+               if (isset($savem)) $this->conn->SetFetchMode($savem);\r
+               $ADODB_CACHE_MODE = $save;\r
+               $this->conn->fnExecute = $saveE;\r
+               return $s;\r
+       }\r
+\r
+       /* \r
+               Explain Plan for $sql.\r
+               If only a snippet of the $sql is passed in, then $partial will hold the crc32 of the \r
+                       actual sql.\r
+       */\r
+       function Explain($sql,$partial=false)\r
+       {       \r
+               return false;\r
+       }\r
+       \r
+       function InvalidSQL($numsql = 10)\r
+       {\r
+       \r
+               if (isset($_GET['sql'])) return;\r
+               $s = '<h3>Invalid SQL</h3>';\r
+               $saveE = $this->conn->fnExecute;\r
+               $this->conn->fnExecute = false;\r
+        $perf_table = adodb_perf::table();\r
+               $rs =& $this->conn->SelectLimit("select distinct count(*),sql1,tracer as error_msg from $perf_table where tracer like 'ERROR:%' group by sql1,tracer order by 1 desc",$numsql);//,$numsql);\r
+               $this->conn->fnExecute = $saveE;\r
+               if ($rs) {\r
+                       $s .= rs2html($rs,false,false,false,false);\r
+               } else\r
+                       return "<p>$this->helpurl. ".$this->conn->ErrorMsg()."</p>";\r
+               \r
+               return $s;\r
+       }\r
+\r
+       \r
+       /*\r
+               This script identifies the longest running SQL\r
+       */      \r
+       function _SuspiciousSQL($numsql = 10)\r
+       {\r
+               global $ADODB_FETCH_MODE;\r
+               \r
+            $perf_table = adodb_perf::table();\r
+                       $saveE = $this->conn->fnExecute;\r
+                       $this->conn->fnExecute = false;\r
+                       \r
+                       if (isset($_GET['exps']) && isset($_GET['sql'])) {\r
+                               $partial = !empty($_GET['part']);\r
+                               echo "<a name=explain></a>".$this->Explain($_GET['sql'],$partial)."\n";\r
+                       }\r
+                       \r
+                       if (isset($_GET['sql'])) return;\r
+                       $sql1 = $this->sql1;\r
+                       \r
+                       $save = $ADODB_FETCH_MODE;\r
+                       $ADODB_FETCH_MODE = ADODB_FETCH_NUM;\r
+                       if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);\r
+                       //$this->conn->debug=1;\r
+                       $rs =& $this->conn->SelectLimit(\r
+                       "select avg(timer) as avg_timer,$sql1,count(*),max(timer) as max_timer,min(timer) as min_timer\r
+                               from $perf_table\r
+                               where {$this->conn->upperCase}({$this->conn->substr}(sql0,1,5)) not in ('DROP ','INSER','COMMI','CREAT')\r
+                               and (tracer is null or tracer not like 'ERROR:%')\r
+                               group by sql1\r
+                               order by 1 desc",$numsql);\r
+                       if (isset($savem)) $this->conn->SetFetchMode($savem);\r
+                       $ADODB_FETCH_MODE = $save;\r
+                       $this->conn->fnExecute = $saveE;\r
+                       \r
+                       if (!$rs) return "<p>$this->helpurl. ".$this->conn->ErrorMsg()."</p>";\r
+                       $s = "<h3>Suspicious SQL</h3>\r
+<font size=1>The following SQL have high average execution times</font><br>\r
+<table border=1 bgcolor=white><tr><td><b>Avg Time</b><td><b>Count</b><td><b>SQL</b><td><b>Max</b><td><b>Min</b></tr>\n";\r
+                       $max = $this->maxLength;\r
+                       while (!$rs->EOF) {\r
+                               $sql = $rs->fields[1];\r
+                               $raw = urlencode($sql);\r
+                               if (strlen($raw)>$max-100) {\r
+                                       $sql2 = substr($sql,0,$max-500);\r
+                                       $raw = urlencode($sql2).'&part='.crc32($sql);\r
+                               }\r
+                               $prefix = "<a target=sql".rand()." href=\"?hidem=1&exps=1&sql=".$raw."&x#explain\">";\r
+                               $suffix = "</a>";\r
+                               if ($this->explain == false || strlen($prefix)>$max) {\r
+                                       $suffix = ' ... <i>String too long for GET parameter: '.strlen($prefix).'</i>';\r
+                                       $prefix = '';\r
+                               }\r
+                               $s .= "<tr><td>".adodb_round($rs->fields[0],6)."<td align=right>".$rs->fields[2]."<td><font size=-1>".$prefix.htmlspecialchars($sql).$suffix."</font>".\r
+                                       "<td>".$rs->fields[3]."<td>".$rs->fields[4]."</tr>";\r
+                               $rs->MoveNext();\r
+                       }\r
+                       return $s."</table>";\r
+               \r
+       }\r
+       \r
+       function CheckMemory()\r
+       {\r
+               return '';\r
+       }\r
+       \r
+       \r
+       function SuspiciousSQL($numsql=10)\r
+       {\r
+               return adodb_perf::_SuspiciousSQL($numsql);\r
+       }\r
+\r
+       function ExpensiveSQL($numsql=10)\r
+       {\r
+               return adodb_perf::_ExpensiveSQL($numsql);\r
+       }\r
+\r
+       \r
+       /*\r
+               This reports the percentage of load on the instance due to the most \r
+               expensive few SQL statements. Tuning these statements can often \r
+               make huge improvements in overall system performance. \r
+       */\r
+       function _ExpensiveSQL($numsql = 10)\r
+       {\r
+               global $ADODB_FETCH_MODE;\r
+               \r
+            $perf_table = adodb_perf::table();\r
+                       $saveE = $this->conn->fnExecute;\r
+                       $this->conn->fnExecute = false;\r
+                       \r
+                       if (isset($_GET['expe']) && isset($_GET['sql'])) {\r
+                               $partial = !empty($_GET['part']);\r
+                               echo "<a name=explain></a>".$this->Explain($_GET['sql'],$partial)."\n";\r
+                       }\r
+                       \r
+                       if (isset($_GET['sql'])) return;\r
+                       \r
+                       $sql1 = $this->sql1;\r
+                       $save = $ADODB_FETCH_MODE;\r
+                       $ADODB_FETCH_MODE = ADODB_FETCH_NUM;\r
+                       if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);\r
+                       \r
+                       $rs =& $this->conn->SelectLimit(\r
+                       "select sum(timer) as total,$sql1,count(*),max(timer) as max_timer,min(timer) as min_timer\r
+                               from $perf_table\r
+                               where {$this->conn->upperCase}({$this->conn->substr}(sql0,1,5))  not in ('DROP ','INSER','COMMI','CREAT')\r
+                               and (tracer is null or tracer not like 'ERROR:%')\r
+                               group by sql1\r
+                               having count(*)>1\r
+                               order by 1 desc",$numsql);\r
+                       if (isset($savem)) $this->conn->SetFetchMode($savem);\r
+                       $this->conn->fnExecute = $saveE;\r
+                       $ADODB_FETCH_MODE = $save;\r
+                       if (!$rs) return "<p>$this->helpurl. ".$this->conn->ErrorMsg()."</p>";\r
+                       $s = "<h3>Expensive SQL</h3>\r
+<font size=1>Tuning the following SQL could reduce the server load substantially</font><br>\r
+<table border=1 bgcolor=white><tr><td><b>Load</b><td><b>Count</b><td><b>SQL</b><td><b>Max</b><td><b>Min</b></tr>\n";\r
+                       $max = $this->maxLength;\r
+                       while (!$rs->EOF) {\r
+                               $sql = $rs->fields[1];\r
+                               $raw = urlencode($sql);\r
+                               if (strlen($raw)>$max-100) {\r
+                                       $sql2 = substr($sql,0,$max-500);\r
+                                       $raw = urlencode($sql2).'&part='.crc32($sql);\r
+                               }\r
+                               $prefix = "<a target=sqle".rand()." href=\"?hidem=1&expe=1&sql=".$raw."&x#explain\">";\r
+                               $suffix = "</a>";\r
+                               if($this->explain == false || strlen($prefix>$max)) {\r
+                                       $prefix = '';\r
+                                       $suffix = '';\r
+                               }\r
+                               $s .= "<tr><td>".adodb_round($rs->fields[0],6)."<td align=right>".$rs->fields[2]."<td><font size=-1>".$prefix.htmlspecialchars($sql).$suffix."</font>".\r
+                                       "<td>".$rs->fields[3]."<td>".$rs->fields[4]."</tr>";\r
+                               $rs->MoveNext();\r
+                       }\r
+                       return $s."</table>";\r
+       }\r
+       \r
+       /*\r
+               Raw function to return parameter value from $settings.\r
+       */\r
+       function DBParameter($param)\r
+       {\r
+               if (empty($this->settings[$param])) return false;\r
+               $sql = $this->settings[$param][1];\r
+               return $this->_DBParameter($sql);\r
+       }\r
+       \r
+       /*\r
+               Raw function returning array of poll paramters\r
+       */\r
+       function &PollParameters()\r
+       {\r
+               $arr[0] = (float)$this->DBParameter('data cache hit ratio');\r
+               $arr[1] = (float)$this->DBParameter('data reads');\r
+               $arr[2] = (float)$this->DBParameter('data writes');\r
+               $arr[3] = (integer) $this->DBParameter('current connections');\r
+               return $arr;\r
+       }\r
+       \r
+       /*\r
+               Low-level Get Database Parameter\r
+       */\r
+       function _DBParameter($sql)\r
+       {\r
+               $savelog = $this->conn->LogSQL(false);\r
+               if (is_array($sql)) {\r
+               global $ADODB_FETCH_MODE;\r
+               \r
+                       $sql1 = $sql[0];\r
+                       $key = $sql[1];\r
+                       if (sizeof($sql)>2) $pos = $sql[2];\r
+                       else $pos = 1;\r
+                       if (sizeof($sql)>3) $coef = $sql[3];\r
+                       else $coef = false;\r
+                       $ret = false;\r
+                       $save = $ADODB_FETCH_MODE;\r
+                       $ADODB_FETCH_MODE = ADODB_FETCH_NUM;\r
+                       if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);\r
+                       \r
+                       $rs = $this->conn->Execute($sql1);\r
+                       \r
+                       if (isset($savem)) $this->SetFetchMode($savem);\r
+                       $ADODB_FETCH_MODE = $save;\r
+                       if ($rs) {\r
+                               while (!$rs->EOF) {\r
+                                       $keyf = reset($rs->fields);\r
+                                       if (trim($keyf) == $key) {\r
+                                               $ret = $rs->fields[$pos];\r
+                                               if ($coef) $ret *= $coef;\r
+                                               break;\r
+                                       }\r
+                                       $rs->MoveNext();\r
+                               }\r
+                               $rs->Close();\r
+                       }\r
+                       $this->conn->LogSQL($savelog);\r
+                       return $ret;\r
+               } else {\r
+                       if (strncmp($sql,'=',1) == 0) {\r
+                               $fn = substr($sql,1);\r
+                               return $this->$fn();\r
+                       }\r
+                       $sql = str_replace('$DATABASE',$this->conn->database,$sql);\r
+                       $ret = $this->conn->GetOne($sql);\r
+                       $this->conn->LogSQL($savelog);\r
+                       \r
+                       return $ret;\r
+               }\r
+       }\r
+       \r
+       /*\r
+               Warn if cache ratio falls below threshold. Displayed in "Description" column.\r
+       */\r
+       function WarnCacheRatio($val)\r
+       {\r
+               if ($val < $this->warnRatio) \r
+                        return '<font color=red><b>Cache ratio should be at least '.$this->warnRatio.'%</b></font>';\r
+               else return '';\r
+       }\r
+       \r
+       /***********************************************************************************************/\r
+       //                                    HIGH LEVEL UI FUNCTIONS\r
+       /***********************************************************************************************/\r
+\r
+       \r
+       function UI($pollsecs=5)\r
+       {\r
+       \r
+    $perf_table = adodb_perf::table();\r
+       $conn = $this->conn;\r
+       \r
+       $app = $conn->host;\r
+       if ($conn->host && $conn->database) $app .= ', db=';\r
+       $app .= $conn->database;\r
+       \r
+       if ($app) $app .= ', ';\r
+       $savelog = $this->conn->LogSQL(false);  \r
+       $info = $conn->ServerInfo();\r
+       if (isset($_GET['clearsql'])) {\r
+               $this->conn->Execute("delete from $perf_table");\r
+       }\r
+       $this->conn->LogSQL($savelog);\r
+       \r
+       // magic quotes\r
+       \r
+       if (isset($_GET['sql']) && get_magic_quotes_gpc()) {\r
+               $_GET['sql'] = $_GET['sql'] = str_replace(array("\\'",'\"'),array("'",'"'),$_GET['sql']);\r
+       }\r
+       \r
+       if (!isset($_SESSION['ADODB_PERF_SQL'])) $nsql = $_SESSION['ADODB_PERF_SQL'] = 10;\r
+       else  $nsql = $_SESSION['ADODB_PERF_SQL'];\r
+       \r
+       $app .= $info['description'];\r
+       \r
+       \r
+       if (isset($_GET['do'])) $do = $_GET['do'];\r
+       else if (isset($_POST['do'])) $do = $_POST['do'];\r
+        else if (isset($_GET['sql'])) $do = 'viewsql';\r
+        else $do = 'stats';\r
+        \r
+       if (isset($_GET['nsql'])) {\r
+               if ($_GET['nsql'] > 0) $nsql = $_SESSION['ADODB_PERF_SQL'] = (integer) $_GET['nsql'];\r
+       }\r
+       echo "<title>ADOdb Performance Monitor on $app</title><body bgcolor=white>";\r
+       if ($do == 'viewsql') $form = "<td><form># SQL:<input type=hidden value=viewsql name=do> <input type=text size=4 name=nsql value=$nsql><input type=submit value=Go></td></form>";\r
+       else $form = "<td>&nbsp;</td>";\r
+       \r
+       $allowsql = !defined('ADODB_PERF_NO_RUN_SQL');\r
+       \r
+       if  (empty($_GET['hidem']))\r
+       echo "<table border=1 width=100% bgcolor=lightyellow><tr><td colspan=2>\r
+       <b><a href=http://adodb.sourceforge.net/?perf=1>ADOdb</a> Performance Monitor</b> <font size=1>for $app</font></tr><tr><td>\r
+       <a href=?do=stats><b>Performance Stats</b></a> &nbsp; <a href=?do=viewsql><b>View SQL</b></a>\r
+        &nbsp; <a href=?do=tables><b>View Tables</b></a> &nbsp; <a href=?do=poll><b>Poll Stats</b></a>",\r
+        $allowsql ? ' &nbsp; <a href=?do=dosql><b>Run SQL</b></a>' : '',\r
+        "$form",\r
+        "</tr></table>";\r
+\r
+        \r
+               switch ($do) {\r
+               default:\r
+               case 'stats':\r
+                       echo $this->HealthCheck();\r
+                       //$this->conn->debug=1;\r
+                       echo $this->CheckMemory();\r
+                       break;\r
+               case 'poll':\r
+                       echo "<iframe width=720 height=80% \r
+                               src=\"{$_SERVER['PHP_SELF']}?do=poll2&hidem=1\"></iframe>";\r
+                       break;\r
+               case 'poll2':\r
+                       echo "<pre>";\r
+                       $this->Poll($pollsecs);\r
+                       break;\r
+               \r
+               case 'dosql':\r
+                       if (!$allowsql) break;\r
+                       \r
+                       $this->DoSQLForm();\r
+                       break;\r
+               case 'viewsql':\r
+                       if (empty($_GET['hidem']))\r
+                               echo "&nbsp; <a href=\"?do=viewsql&clearsql=1\">Clear SQL Log</a><br>";\r
+                       echo($this->SuspiciousSQL($nsql));\r
+                       echo($this->ExpensiveSQL($nsql));\r
+                       echo($this->InvalidSQL($nsql));\r
+                       break;\r
+               case 'tables': \r
+                       echo $this->Tables(); break;\r
+               }\r
+               global $ADODB_vers;\r
+               echo "<p><div align=center><font size=1>$ADODB_vers Sponsored by <a href=http://phplens.com/>phpLens</a></font></div>";\r
+       }\r
+       \r
+       /*\r
+               Runs in infinite loop, returning real-time statistics\r
+       */\r
+       function Poll($secs=5)\r
+       {\r
+               $this->conn->fnExecute = false;\r
+               //$this->conn->debug=1;\r
+               if ($secs <= 1) $secs = 1;\r
+               echo "Accumulating statistics, every $secs seconds...\n";flush();\r
+               $arro =& $this->PollParameters();\r
+               $cnt = 0;\r
+               set_time_limit(0);\r
+               sleep($secs);\r
+               while (1) {\r
+\r
+                       $arr =& $this->PollParameters();\r
+                       \r
+                       $hits   = sprintf('%2.2f',$arr[0]);\r
+                       $reads  = sprintf('%12.4f',($arr[1]-$arro[1])/$secs);\r
+                       $writes = sprintf('%12.4f',($arr[2]-$arro[2])/$secs);\r
+                       $sess = sprintf('%5d',$arr[3]);\r
+                       \r
+                       $load = $this->CPULoad();\r
+                       if ($load !== false) {\r
+                               $oslabel = 'WS-CPU%';\r
+                               $osval = sprintf(" %2.1f  ",(float) $load);\r
+                       }else {\r
+                               $oslabel = '';\r
+                               $osval = '';\r
+                       }\r
+                       if ($cnt % 10 == 0) echo " Time   ".$oslabel."   Hit%   Sess           Reads/s          Writes/s\n"; \r
+                       $cnt += 1;\r
+                       echo date('H:i:s').'  '.$osval."$hits  $sess $reads $writes\n";\r
+                       flush();\r
+                       \r
+                       if (connection_aborted()) return;\r
+                       \r
+                       sleep($secs);\r
+                       $arro = $arr;\r
+               }\r
+       }\r
+       \r
+       /*\r
+               Returns basic health check in a command line interface\r
+       */\r
+       function HealthCheckCLI()\r
+       {\r
+               return $this->HealthCheck(true);\r
+       }\r
+       \r
+               \r
+       /*\r
+               Returns basic health check as HTML\r
+       */\r
+       function HealthCheck($cli=false)\r
+       {\r
+               $saveE = $this->conn->fnExecute;\r
+               $this->conn->fnExecute = false; \r
+               if ($cli) $html = '';\r
+               else $html = $this->table.'<tr><td colspan=3><h3>'.$this->conn->databaseType.'</h3></td></tr>'.$this->titles;\r
+               \r
+               $oldc = false;\r
+               $bgc = '';\r
+               foreach($this->settings as $name => $arr) {\r
+                       if ($arr === false) break;\r
+                       \r
+                       if (!is_string($name)) {\r
+                               if ($cli) $html .= " -- $arr -- \n";\r
+                               else $html .= "<tr bgcolor=$this->color><td colspan=3><i>$arr</i> &nbsp;</td></tr>";\r
+                               continue;\r
+                       }\r
+                       \r
+                       if (!is_array($arr)) break;\r
+                       $category = $arr[0];\r
+                       $how = $arr[1];\r
+                       if (sizeof($arr)>2) $desc = $arr[2];\r
+                       else $desc = ' &nbsp; ';\r
+                       \r
+                       \r
+                       if ($category == 'HIDE') continue;\r
+                       \r
+                       $val = $this->_DBParameter($how);\r
+                       \r
+                       if ($desc && strncmp($desc,"=",1) === 0) {\r
+                               $fn = substr($desc,1);\r
+                               $desc = $this->$fn($val);\r
+                       }\r
+                       \r
+                       if ($val === false) {\r
+                               $m = $this->conn->ErrorMsg();\r
+                               $val = "Error: $m"; \r
+                       } else {\r
+                               if (is_numeric($val) && $val >= 256*1024) {\r
+                                       if ($val % (1024*1024) == 0) {\r
+                                               $val /= (1024*1024);\r
+                                               $val .= 'M';\r
+                                       } else if ($val % 1024 == 0) {\r
+                                               $val /= 1024;\r
+                                               $val .= 'K';\r
+                                       }\r
+                                       //$val = htmlspecialchars($val);\r
+                               }\r
+                       }\r
+                       if ($category != $oldc) {\r
+                               $oldc = $category;\r
+                               //$bgc = ($bgc == ' bgcolor='.$this->color) ? ' bgcolor=white' : ' bgcolor='.$this->color;\r
+                       }\r
+                       if (strlen($desc)==0) $desc = '&nbsp;';\r
+                       if (strlen($val)==0) $val = '&nbsp;';\r
+                       if ($cli) {\r
+                               $html  .= str_replace('&nbsp;','',sprintf($this->cliFormat,strip_tags($name),strip_tags($val),strip_tags($desc)));\r
+                               \r
+                       }else {\r
+                               $html .= "<tr$bgc><td>".$name.'</td><td>'.$val.'</td><td>'.$desc."</td></tr>\n";\r
+                       }\r
+               }\r
+               \r
+               if (!$cli) $html .= "</table>\n";\r
+               $this->conn->fnExecute = $saveE;\r
+                       \r
+               return $html;   \r
+       }\r
+       \r
+       function Tables($orderby='1')\r
+       {\r
+               if (!$this->tablesSQL) return false;\r
+               \r
+               $savelog = $this->conn->LogSQL(false);\r
+               $rs = $this->conn->Execute($this->tablesSQL.' order by '.$orderby);\r
+               $this->conn->LogSQL($savelog);\r
+               $html = rs2html($rs,false,false,false,false);\r
+               return $html;\r
+       }\r
+       \r
+\r
+       function CreateLogTable()\r
+       {\r
+               if (!$this->createTableSQL) return false;\r
+               \r
+               $savelog = $this->conn->LogSQL(false);\r
+               $ok = $this->conn->Execute($this->createTableSQL);\r
+               $this->conn->LogSQL($savelog);\r
+               return ($ok) ? true : false;\r
+       }\r
+       \r
+       function DoSQLForm()\r
+       {\r
+       \r
+               \r
+               $PHP_SELF = $_SERVER['PHP_SELF'];\r
+               $sql = isset($_REQUEST['sql']) ? $_REQUEST['sql'] : '';\r
+\r
+               if (isset($_SESSION['phplens_sqlrows'])) $rows = $_SESSION['phplens_sqlrows'];\r
+               else $rows = 3;\r
+               \r
+               if (isset($_REQUEST['SMALLER'])) {\r
+                       $rows /= 2;\r
+                       if ($rows < 3) $rows = 3;\r
+                       $_SESSION['phplens_sqlrows'] = $rows;\r
+               }\r
+               if (isset($_REQUEST['BIGGER'])) {\r
+                       $rows *= 2;\r
+                       $_SESSION['phplens_sqlrows'] = $rows;\r
+               }\r
+               \r
+?>\r
+\r
+<form method="POST" action="<?php echo $PHP_SELF ?>">\r
+<table><tr>\r
+<td> Form size: <input type="submit" value=" &lt; " name="SMALLER"><input type="submit" value=" &gt; &gt; " name="BIGGER">\r
+</td>\r
+<td align=right>\r
+<input type="submit" value=" Run SQL Below " name="RUN"><input type=hidden name=do value=dosql>\r
+</td></tr>\r
+  <tr>\r
+  <td colspan=2><textarea rows=<?php print $rows; ?> name="sql" cols="80"><?php print htmlspecialchars($sql) ?></textarea>\r
+  </td>\r
+  </tr>\r
+ </table>\r
+</form>\r
+\r
+<?php\r
+               if (!isset($_REQUEST['sql'])) return;\r
+               \r
+               $sql = $this->undomq(trim($sql));\r
+               if (substr($sql,strlen($sql)-1) === ';') {\r
+                       $print = true;\r
+                       $sqla = $this->SplitSQL($sql);\r
+               } else  {\r
+                       $print = false;\r
+                       $sqla = array($sql);\r
+               }\r
+               foreach($sqla as $sqls) {\r
+\r
+                       if (!$sqls) continue;\r
+                       \r
+                       if ($print) {\r
+                               print "<p>".htmlspecialchars($sqls)."</p>";\r
+                               flush();\r
+                       }\r
+                       $savelog = $this->conn->LogSQL(false);\r
+                       $rs = $this->conn->Execute($sqls);\r
+                       $this->conn->LogSQL($savelog);\r
+                       if ($rs && is_object($rs) && !$rs->EOF) {\r
+                               rs2html($rs);\r
+                               while ($rs->NextRecordSet()) {\r
+                                       print "<table width=98% bgcolor=#C0C0FF><tr><td>&nbsp;</td></tr></table>";\r
+                                       rs2html($rs);\r
+                               }\r
+                       } else {\r
+                               $e1 = (integer) $this->conn->ErrorNo();\r
+                               $e2 = $this->conn->ErrorMsg();\r
+                               if (($e1) || ($e2)) {\r
+                                       if (empty($e1)) $e1 = '-1'; // postgresql fix\r
+                                       print ' &nbsp; '.$e1.': '.$e2;\r
+                               } else {\r
+                                       print "<p>No Recordset returned<br></p>";\r
+                               }\r
+                       }\r
+               } // foreach\r
+       }\r
+       \r
+       function SplitSQL($sql)\r
+       {\r
+               $arr = explode(';',$sql);\r
+               return $arr;\r
+       }\r
+       \r
+       function undomq(&$m) \r
+       {\r
+       if (get_magic_quotes_gpc()) {\r
+               // undo the damage\r
+               $m = str_replace('\\\\','\\',$m);\r
+               $m = str_replace('\"','"',$m);\r
+               $m = str_replace('\\\'','\'',$m);\r
+       }\r
+       return $m;\r
+}\r
+\r
+    \r
+   /************************************************************************/\r
+   \r
+    /** \r
+     * Reorganise multiple table-indices/statistics/..\r
+     * OptimizeMode could be given by last Parameter\r
+     * \r
+     * @example\r
+     *      <pre>\r
+     *          optimizeTables( 'tableA');\r
+     *      </pre>\r
+     *      <pre>\r
+     *          optimizeTables( 'tableA', 'tableB', 'tableC');\r
+     *      </pre>\r
+     *      <pre>\r
+     *          optimizeTables( 'tableA', 'tableB', ADODB_OPT_LOW);\r
+     *      </pre>\r
+     * \r
+     * @param string table name of the table to optimize\r
+     * @param int mode optimization-mode\r
+     *      <code>ADODB_OPT_HIGH</code> for full optimization \r
+     *      <code>ADODB_OPT_LOW</code> for CPU-less optimization\r
+     *      Default is LOW <code>ADODB_OPT_LOW</code> \r
+     * @author Markus Staab\r
+     * @return Returns <code>true</code> on success and <code>false</code> on error\r
+     */\r
+    function OptimizeTables()\r
+    {\r
+        $args = func_get_args();\r
+        $numArgs = func_num_args();\r
+        \r
+        if ( $numArgs == 0) return false;\r
+        \r
+        $mode = ADODB_OPT_LOW; \r
+        $lastArg = $args[ $numArgs - 1];\r
+        if ( !is_string($lastArg)) {\r
+            $mode = $lastArg;\r
+            unset( $args[ $numArgs - 1]);\r
+        }\r
+        \r
+        foreach( $args as $table) {\r
+            $this->optimizeTable( $table, $mode);\r
+        }\r
+       }\r
+\r
+    /** \r
+     * Reorganise the table-indices/statistics/.. depending on the given mode.\r
+     * Default Implementation throws an error.\r
+     * \r
+     * @param string table name of the table to optimize\r
+     * @param int mode optimization-mode\r
+     *      <code>ADODB_OPT_HIGH</code> for full optimization \r
+     *      <code>ADODB_OPT_LOW</code> for CPU-less optimization\r
+     *      Default is LOW <code>ADODB_OPT_LOW</code> \r
+     * @author Markus Staab\r
+     * @return Returns <code>true</code> on success and <code>false</code> on error\r
+     */\r
+    function OptimizeTable( $table, $mode = ADODB_OPT_LOW) \r
+    {\r
+        ADOConnection::outp( sprintf( "<p>%s: '%s' not implemented for driver '%s'</p>", __CLASS__, __FUNCTION__, $this->conn->databaseType));\r
+        return false;\r
+    }\r
+    \r
+    /** \r
+     * Reorganise current database.\r
+     * Default implementation loops over all <code>MetaTables()</code> and \r
+     * optimize each using <code>optmizeTable()</code>\r
+     * \r
+     * @author Markus Staab\r
+     * @return Returns <code>true</code> on success and <code>false</code> on error\r
+     */\r
+    function optimizeDatabase() \r
+    {\r
+        $conn = $this->conn;\r
+        if ( !$conn) return false;\r
+        \r
+        $tables = $conn->MetaTables( 'TABLES');\r
+        if ( !$tables ) return false;\r
+\r
+        foreach( $tables as $table) {\r
+            if ( !$this->optimizeTable( $table)) {\r
+                return false;\r
+            }\r
+        }\r
+      \r
+        return true;\r
+    }\r
+    // end hack \r
+}\r
+\r
+?>
\ No newline at end of file
diff --git a/libraries/adodb/adodb-php4.inc.php b/libraries/adodb/adodb-php4.inc.php
new file mode 100755 (executable)
index 0000000..6059347
--- /dev/null
@@ -0,0 +1,16 @@
+<?php\r
+\r
+/*\r
+  V4.65 22 July 2005  (c) 2000-2005 John Lim (jlim@natsoft.com.my). All rights reserved.\r
+  Released under both BSD license and Lesser GPL library license. \r
+  Whenever there is any discrepancy between the two licenses, \r
+  the BSD license will take precedence.\r
+  \r
+  Set tabs to 4.\r
+*/\r
+\r
+\r
+class ADODB_BASE_RS {\r
+}\r
+\r
+?>
\ No newline at end of file
diff --git a/libraries/adodb/adodb-time.inc.php b/libraries/adodb/adodb-time.inc.php
new file mode 100644 (file)
index 0000000..4385409
--- /dev/null
@@ -0,0 +1,1287 @@
+<?php\r
+/**\r
+ADOdb Date Library, part of the ADOdb abstraction library\r
+Download: http://php.weblogs.com/adodb_date_time_library\r
+\r
+PHP native date functions use integer timestamps for computations.\r
+Because of this, dates are restricted to the years 1901-2038 on Unix \r
+and 1970-2038 on Windows due to integer overflow for dates beyond \r
+those years. This library overcomes these limitations by replacing the \r
+native function's signed integers (normally 32-bits) with PHP floating \r
+point numbers (normally 64-bits).\r
+\r
+Dates from 100 A.D. to 3000 A.D. and later\r
+have been tested. The minimum is 100 A.D. as <100 will invoke the\r
+2 => 4 digit year conversion. The maximum is billions of years in the \r
+future, but this is a theoretical limit as the computation of that year \r
+would take too long with the current implementation of adodb_mktime().\r
+\r
+This library replaces native functions as follows:\r
+\r
+<pre>  \r
+       getdate()  with  adodb_getdate()\r
+       date()     with  adodb_date() \r
+       gmdate()   with  adodb_gmdate()\r
+       mktime()   with  adodb_mktime()\r
+       gmmktime() with  adodb_gmmktime()\r
+       strftime() with  adodb_strftime()\r
+       strftime() with  adodb_gmstrftime()\r
+</pre>\r
+       \r
+The parameters are identical, except that adodb_date() accepts a subset\r
+of date()'s field formats. Mktime() will convert from local time to GMT, \r
+and date() will convert from GMT to local time, but daylight savings is \r
+not handled currently.\r
+\r
+This library is independant of the rest of ADOdb, and can be used\r
+as standalone code.\r
+\r
+PERFORMANCE\r
+\r
+For high speed, this library uses the native date functions where\r
+possible, and only switches to PHP code when the dates fall outside \r
+the 32-bit signed integer range.\r
+\r
+GREGORIAN CORRECTION\r
+\r
+Pope Gregory shortened October of A.D. 1582 by ten days. Thursday, \r
+October 4, 1582 (Julian) was followed immediately by Friday, October 15, \r
+1582 (Gregorian). \r
+\r
+Since 0.06, we handle this correctly, so:\r
+\r
+adodb_mktime(0,0,0,10,15,1582) - adodb_mktime(0,0,0,10,4,1582) \r
+       == 24 * 3600 (1 day)\r
+\r
+=============================================================================\r
+\r
+COPYRIGHT\r
+\r
+(c) 2003-2005 John Lim and released under BSD-style license except for code by \r
+jackbbs, which includes adodb_mktime, adodb_get_gmt_diff, adodb_is_leap_year\r
+and originally found at http://www.php.net/manual/en/function.mktime.php\r
+\r
+=============================================================================\r
+\r
+BUG REPORTS\r
+\r
+These should be posted to the ADOdb forums at\r
+\r
+       http://phplens.com/lens/lensforum/topics.php?id=4\r
+\r
+=============================================================================\r
+\r
+FUNCTION DESCRIPTIONS\r
+\r
+\r
+** FUNCTION adodb_getdate($date=false)\r
+\r
+Returns an array containing date information, as getdate(), but supports\r
+dates greater than 1901 to 2038. The local date/time format is derived from a \r
+heuristic the first time adodb_getdate is called. \r
+        \r
+        \r
+** FUNCTION adodb_date($fmt, $timestamp = false)\r
+\r
+Convert a timestamp to a formatted local date. If $timestamp is not defined, the\r
+current timestamp is used. Unlike the function date(), it supports dates\r
+outside the 1901 to 2038 range.\r
+\r
+The format fields that adodb_date supports:\r
+\r
+<pre>\r
+       a - "am" or "pm" \r
+       A - "AM" or "PM" \r
+       d - day of the month, 2 digits with leading zeros; i.e. "01" to "31" \r
+       D - day of the week, textual, 3 letters; e.g. "Fri" \r
+       F - month, textual, long; e.g. "January" \r
+       g - hour, 12-hour format without leading zeros; i.e. "1" to "12" \r
+       G - hour, 24-hour format without leading zeros; i.e. "0" to "23" \r
+       h - hour, 12-hour format; i.e. "01" to "12" \r
+       H - hour, 24-hour format; i.e. "00" to "23" \r
+       i - minutes; i.e. "00" to "59" \r
+       j - day of the month without leading zeros; i.e. "1" to "31" \r
+       l (lowercase 'L') - day of the week, textual, long; e.g. "Friday"  \r
+       L - boolean for whether it is a leap year; i.e. "0" or "1" \r
+       m - month; i.e. "01" to "12" \r
+       M - month, textual, 3 letters; e.g. "Jan" \r
+       n - month without leading zeros; i.e. "1" to "12" \r
+       O - Difference to Greenwich time in hours; e.g. "+0200" \r
+       Q - Quarter, as in 1, 2, 3, 4 \r
+       r - RFC 822 formatted date; e.g. "Thu, 21 Dec 2000 16:01:07 +0200" \r
+       s - seconds; i.e. "00" to "59" \r
+       S - English ordinal suffix for the day of the month, 2 characters; \r
+                               i.e. "st", "nd", "rd" or "th" \r
+       t - number of days in the given month; i.e. "28" to "31"\r
+       T - Timezone setting of this machine; e.g. "EST" or "MDT" \r
+       U - seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)  \r
+       w - day of the week, numeric, i.e. "0" (Sunday) to "6" (Saturday) \r
+       Y - year, 4 digits; e.g. "1999" \r
+       y - year, 2 digits; e.g. "99" \r
+       z - day of the year; i.e. "0" to "365" \r
+       Z - timezone offset in seconds (i.e. "-43200" to "43200"). \r
+                               The offset for timezones west of UTC is always negative, \r
+                               and for those east of UTC is always positive. \r
+</pre>\r
+\r
+Unsupported:\r
+<pre>\r
+       B - Swatch Internet time \r
+       I (capital i) - "1" if Daylight Savings Time, "0" otherwise.\r
+       W - ISO-8601 week number of year, weeks starting on Monday \r
+\r
+</pre>\r
+\r
+\r
+** FUNCTION adodb_date2($fmt, $isoDateString = false)\r
+Same as adodb_date, but 2nd parameter accepts iso date, eg.\r
+\r
+  adodb_date2('d-M-Y H:i','2003-12-25 13:01:34');\r
+\r
+  \r
+** FUNCTION adodb_gmdate($fmt, $timestamp = false)\r
+\r
+Convert a timestamp to a formatted GMT date. If $timestamp is not defined, the\r
+current timestamp is used. Unlike the function date(), it supports dates\r
+outside the 1901 to 2038 range.\r
+\r
+\r
+** FUNCTION adodb_mktime($hr, $min, $sec[, $month, $day, $year])\r
+\r
+Converts a local date to a unix timestamp.  Unlike the function mktime(), it supports\r
+dates outside the 1901 to 2038 range. All parameters are optional.\r
+\r
+\r
+** FUNCTION adodb_gmmktime($hr, $min, $sec [, $month, $day, $year])\r
+\r
+Converts a gmt date to a unix timestamp.  Unlike the function gmmktime(), it supports\r
+dates outside the 1901 to 2038 range. Differs from gmmktime() in that all parameters\r
+are currently compulsory.\r
+\r
+** FUNCTION adodb_gmstrftime($fmt, $timestamp = false)\r
+Convert a timestamp to a formatted GMT date.\r
+\r
+** FUNCTION adodb_strftime($fmt, $timestamp = false)\r
+\r
+Convert a timestamp to a formatted local date. Internally converts $fmt into \r
+adodb_date format, then echo result.\r
+\r
+For best results, you can define the local date format yourself. Define a global\r
+variable $ADODB_DATE_LOCALE which is an array, 1st element is date format using\r
+adodb_date syntax, and 2nd element is the time format, also in adodb_date syntax.\r
+\r
+    eg. $ADODB_DATE_LOCALE = array('d/m/Y','H:i:s');\r
+       \r
+       Supported format codes:\r
+\r
+<pre>\r
+       %a - abbreviated weekday name according to the current locale \r
+       %A - full weekday name according to the current locale \r
+       %b - abbreviated month name according to the current locale \r
+       %B - full month name according to the current locale \r
+       %c - preferred date and time representation for the current locale \r
+       %d - day of the month as a decimal number (range 01 to 31) \r
+       %D - same as %m/%d/%y \r
+       %e - day of the month as a decimal number, a single digit is preceded by a space (range ' 1' to '31') \r
+       %h - same as %b\r
+       %H - hour as a decimal number using a 24-hour clock (range 00 to 23) \r
+       %I - hour as a decimal number using a 12-hour clock (range 01 to 12) \r
+       %m - month as a decimal number (range 01 to 12) \r
+       %M - minute as a decimal number \r
+       %n - newline character \r
+       %p - either `am' or `pm' according to the given time value, or the corresponding strings for the current locale \r
+       %r - time in a.m. and p.m. notation \r
+       %R - time in 24 hour notation \r
+       %S - second as a decimal number \r
+       %t - tab character \r
+       %T - current time, equal to %H:%M:%S \r
+       %x - preferred date representation for the current locale without the time \r
+       %X - preferred time representation for the current locale without the date \r
+       %y - year as a decimal number without a century (range 00 to 99) \r
+       %Y - year as a decimal number including the century \r
+       %Z - time zone or name or abbreviation \r
+       %% - a literal `%' character \r
+</pre> \r
+\r
+       Unsupported codes:\r
+<pre>\r
+       %C - century number (the year divided by 100 and truncated to an integer, range 00 to 99) \r
+       %g - like %G, but without the century. \r
+       %G - The 4-digit year corresponding to the ISO week number (see %V). \r
+            This has the same format and value as %Y, except that if the ISO week number belongs \r
+                to the previous or next year, that year is used instead. \r
+       %j - day of the year as a decimal number (range 001 to 366) \r
+       %u - weekday as a decimal number [1,7], with 1 representing Monday \r
+       %U - week number of the current year as a decimal number, starting \r
+           with the first Sunday as the first day of the first week \r
+       %V - The ISO 8601:1988 week number of the current year as a decimal number, \r
+            range 01 to 53, where week 1 is the first week that has at least 4 days in the \r
+                current year, and with Monday as the first day of the week. (Use %G or %g for \r
+                the year component that corresponds to the week number for the specified timestamp.) \r
+       %w - day of the week as a decimal, Sunday being 0 \r
+       %W - week number of the current year as a decimal number, starting with the \r
+            first Monday as the first day of the first week \r
+</pre>\r
+\r
+=============================================================================\r
+\r
+NOTES\r
+\r
+Useful url for generating test timestamps:\r
+       http://www.4webhelp.net/us/timestamp.php\r
+\r
+Possible future optimizations include \r
+\r
+a. Using an algorithm similar to Plauger's in "The Standard C Library" \r
+(page 428, xttotm.c _Ttotm() function). Plauger's algorithm will not \r
+work outside 32-bit signed range, so i decided not to implement it.\r
+\r
+b. Implement daylight savings, which looks awfully complicated, see\r
+       http://webexhibits.org/daylightsaving/\r
+\r
+\r
+CHANGELOG\r
+\r
+- 18 July  2005  0.21\r
+- In PHP 4.3.11, the 'r' format has changed. Leading 0 in day is added. Changed for compat.\r
+- Added support for negative months in adodb_mktime().\r
+\r
+- 24 Feb 2005 0.20\r
+Added limited strftime/gmstrftime support. x10 improvement in performance of adodb_date().\r
+\r
+- 21 Dec 2004 0.17\r
+In adodb_getdate(), the timestamp was accidentally converted to gmt when $is_gmt is false. \r
+Also adodb_mktime(0,0,0) did not work properly. Both fixed thx Mauro.\r
+\r
+- 17 Nov 2004 0.16\r
+Removed intval typecast in adodb_mktime() for secs, allowing:\r
+        adodb_mktime(0,0,0 + 2236672153,1,1,1934);\r
+Suggested by Ryan.\r
+\r
+- 18 July 2004 0.15\r
+All params in adodb_mktime were formerly compulsory. Now only the hour, min, secs is compulsory. \r
+This brings it more in line with mktime (still not identical).\r
+\r
+- 23 June 2004 0.14\r
+\r
+Allow you to define your own daylights savings function, adodb_daylight_sv.\r
+If the function is defined (somewhere in an include), then you can correct for daylights savings.\r
+\r
+In this example, we apply daylights savings in June or July, adding one hour. This is extremely\r
+unrealistic as it does not take into account time-zone, geographic location, current year.\r
+\r
+function adodb_daylight_sv(&$arr, $is_gmt)\r
+{\r
+       if ($is_gmt) return;\r
+       $m = $arr['mon'];\r
+       if ($m == 6 || $m == 7) $arr['hours'] += 1;\r
+}\r
+\r
+This is only called by adodb_date() and not by adodb_mktime(). \r
+\r
+The format of $arr is\r
+Array ( \r
+   [seconds] => 0 \r
+   [minutes] => 0 \r
+   [hours] => 0 \r
+   [mday] => 1      # day of month, eg 1st day of the month\r
+   [mon] => 2       # month (eg. Feb)\r
+   [year] => 2102 \r
+   [yday] => 31     # days in current year\r
+   [leap] =>        # true if leap year\r
+   [ndays] => 28    # no of days in current month\r
+   ) \r
+   \r
+\r
+- 28 Apr 2004 0.13\r
+Fixed adodb_date to properly support $is_gmt. Thx to Dimitar Angelov.\r
+\r
+- 20 Mar 2004 0.12\r
+Fixed month calculation error in adodb_date. 2102-June-01 appeared as 2102-May-32.\r
+\r
+- 26 Oct 2003 0.11\r
+Because of daylight savings problems (some systems apply daylight savings to \r
+January!!!), changed adodb_get_gmt_diff() to ignore daylight savings.\r
+\r
+- 9 Aug 2003 0.10\r
+Fixed bug with dates after 2038. \r
+See http://phplens.com/lens/lensforum/msgs.php?id=6980\r
+\r
+- 1 July 2003 0.09\r
+Added support for Q (Quarter).\r
+Added adodb_date2(), which accepts ISO date in 2nd param\r
+\r
+- 3 March 2003 0.08\r
+Added support for 'S' adodb_date() format char. Added constant ADODB_ALLOW_NEGATIVE_TS\r
+if you want PHP to handle negative timestamps between 1901 to 1969.\r
+\r
+- 27 Feb 2003 0.07\r
+All negative numbers handled by adodb now because of RH 7.3+ problems.\r
+See http://bugs.php.net/bug.php?id=20048&edit=2\r
+\r
+- 4 Feb 2003 0.06\r
+Fixed a typo, 1852 changed to 1582! This means that pre-1852 dates\r
+are now correctly handled.\r
+\r
+- 29 Jan 2003 0.05\r
+\r
+Leap year checking differs under Julian calendar (pre 1582). Also\r
+leap year code optimized by checking for most common case first.\r
+\r
+We also handle month overflow correctly in mktime (eg month set to 13).\r
+\r
+Day overflow for less than one month's days is supported.\r
+\r
+- 28 Jan 2003 0.04\r
+\r
+Gregorian correction handled. In PHP5, we might throw an error if \r
+mktime uses invalid dates around 5-14 Oct 1582. Released with ADOdb 3.10.\r
+Added limbo 5-14 Oct 1582 check, when we set to 15 Oct 1582.\r
+\r
+- 27 Jan 2003 0.03\r
+\r
+Fixed some more month problems due to gmt issues. Added constant ADODB_DATE_VERSION.\r
+Fixed calculation of days since start of year for <1970. \r
+\r
+- 27 Jan 2003 0.02\r
+\r
+Changed _adodb_getdate() to inline leap year checking for better performance.\r
+Fixed problem with time-zones west of GMT +0000.\r
+\r
+- 24 Jan 2003 0.01\r
+\r
+First implementation.\r
+*/\r
+\r
+\r
+/* Initialization */\r
+\r
+/*\r
+       Version Number\r
+*/\r
+define('ADODB_DATE_VERSION',0.21);\r
+\r
+/*\r
+       This code was originally for windows. But apparently this problem happens \r
+       also with Linux, RH 7.3 and later!\r
+       \r
+       glibc-2.2.5-34 and greater has been changed to return -1 for dates <\r
+       1970.  This used to work.  The problem exists with RedHat 7.3 and 8.0\r
+       echo (mktime(0, 0, 0, 1, 1, 1960));  // prints -1\r
+       \r
+       References:\r
+        http://bugs.php.net/bug.php?id=20048&edit=2\r
+        http://lists.debian.org/debian-glibc/2002/debian-glibc-200205/msg00010.html\r
+*/\r
+\r
+if (!defined('ADODB_ALLOW_NEGATIVE_TS')) define('ADODB_NO_NEGATIVE_TS',1);\r
+\r
+function adodb_date_test_date($y1,$m,$d=13)\r
+{\r
+       $t = adodb_mktime(0,0,0,$m,$d,$y1);\r
+       $rez = adodb_date('Y-n-j H:i:s',$t);\r
+       if ("$y1-$m-$d 00:00:00" != $rez) {\r
+               print "<b>$y1 error, expected=$y1-$m-$d 00:00:00, adodb=$rez</b><br>";\r
+               return false;\r
+       }\r
+       return true;\r
+}\r
+\r
+function adodb_date_test_strftime($fmt)\r
+{\r
+       $s1 = strftime($fmt);\r
+       $s2 = adodb_strftime($fmt);\r
+       \r
+       if ($s1 == $s2) return true;\r
+       \r
+       echo "error for $fmt,  strftime=$s1, $adodb=$s2<br>";\r
+       return false;\r
+}\r
+\r
+/**\r
+        Test Suite\r
+*/\r
+function adodb_date_test()\r
+{\r
+       \r
+       error_reporting(E_ALL);\r
+       print "<h4>Testing adodb_date and adodb_mktime. version=".ADODB_DATE_VERSION.' PHP='.PHP_VERSION."</h4>";\r
+       @set_time_limit(0);\r
+       $fail = false;\r
+       \r
+       // This flag disables calling of PHP native functions, so we can properly test the code\r
+       if (!defined('ADODB_TEST_DATES')) define('ADODB_TEST_DATES',1);\r
+       \r
+       adodb_date_test_strftime('%Y %m %x %X');\r
+       adodb_date_test_strftime("%A %d %B %Y");\r
+       adodb_date_test_strftime("%H %M S");\r
+       \r
+       $t = adodb_mktime(0,0,0);\r
+       if (!(adodb_date('Y-m-d') == date('Y-m-d'))) print 'Error in '.adodb_mktime(0,0,0).'<br>';\r
+       \r
+       $t = adodb_mktime(0,0,0,6,1,2102);\r
+       if (!(adodb_date('Y-m-d',$t) == '2102-06-01')) print 'Error in '.adodb_date('Y-m-d',$t).'<br>';\r
+       \r
+       $t = adodb_mktime(0,0,0,2,1,2102);\r
+       if (!(adodb_date('Y-m-d',$t) == '2102-02-01')) print 'Error in '.adodb_date('Y-m-d',$t).'<br>';\r
+       \r
+       \r
+       print "<p>Testing gregorian <=> julian conversion<p>";\r
+       $t = adodb_mktime(0,0,0,10,11,1492);\r
+       //http://www.holidayorigins.com/html/columbus_day.html - Friday check\r
+       if (!(adodb_date('D Y-m-d',$t) == 'Fri 1492-10-11')) print 'Error in Columbus landing<br>';\r
+       \r
+       $t = adodb_mktime(0,0,0,2,29,1500);\r
+       if (!(adodb_date('Y-m-d',$t) == '1500-02-29')) print 'Error in julian leap years<br>';\r
+       \r
+       $t = adodb_mktime(0,0,0,2,29,1700);\r
+       if (!(adodb_date('Y-m-d',$t) == '1700-03-01')) print 'Error in gregorian leap years<br>';\r
+       \r
+       print  adodb_mktime(0,0,0,10,4,1582).' ';\r
+       print adodb_mktime(0,0,0,10,15,1582);\r
+       $diff = (adodb_mktime(0,0,0,10,15,1582) - adodb_mktime(0,0,0,10,4,1582));\r
+       if ($diff != 3600*24) print " <b>Error in gregorian correction = ".($diff/3600/24)." days </b><br>";\r
+               \r
+       print " 15 Oct 1582, Fri=".(adodb_dow(1582,10,15) == 5 ? 'Fri' : '<b>Error</b>')."<br>";\r
+       print " 4 Oct 1582, Thu=".(adodb_dow(1582,10,4) == 4 ? 'Thu' : '<b>Error</b>')."<br>";\r
+       \r
+       print "<p>Testing overflow<p>";\r
+       \r
+       $t = adodb_mktime(0,0,0,3,33,1965);\r
+       if (!(adodb_date('Y-m-d',$t) == '1965-04-02')) print 'Error in day overflow 1 <br>';\r
+       $t = adodb_mktime(0,0,0,4,33,1971);\r
+       if (!(adodb_date('Y-m-d',$t) == '1971-05-03')) print 'Error in day overflow 2 <br>';\r
+       $t = adodb_mktime(0,0,0,1,60,1965);\r
+       if (!(adodb_date('Y-m-d',$t) == '1965-03-01')) print 'Error in day overflow 3 '.adodb_date('Y-m-d',$t).' <br>';\r
+       $t = adodb_mktime(0,0,0,12,32,1965);\r
+       if (!(adodb_date('Y-m-d',$t) == '1966-01-01')) print 'Error in day overflow 4 '.adodb_date('Y-m-d',$t).' <br>';\r
+       $t = adodb_mktime(0,0,0,12,63,1965);\r
+       if (!(adodb_date('Y-m-d',$t) == '1966-02-01')) print 'Error in day overflow 5 '.adodb_date('Y-m-d',$t).' <br>';\r
+       $t = adodb_mktime(0,0,0,13,3,1965);\r
+       if (!(adodb_date('Y-m-d',$t) == '1966-01-03')) print 'Error in mth overflow 1 <br>';\r
+       \r
+       print "Testing 2-digit => 4-digit year conversion<p>";\r
+       if (adodb_year_digit_check(00) != 2000) print "Err 2-digit 2000<br>";\r
+       if (adodb_year_digit_check(10) != 2010) print "Err 2-digit 2010<br>";\r
+       if (adodb_year_digit_check(20) != 2020) print "Err 2-digit 2020<br>";\r
+       if (adodb_year_digit_check(30) != 2030) print "Err 2-digit 2030<br>";\r
+       if (adodb_year_digit_check(40) != 1940) print "Err 2-digit 1940<br>";\r
+       if (adodb_year_digit_check(50) != 1950) print "Err 2-digit 1950<br>";\r
+       if (adodb_year_digit_check(90) != 1990) print "Err 2-digit 1990<br>";\r
+       \r
+       // Test string formating\r
+       print "<p>Testing date formating</p>";\r
+       $fmt = '\d\a\t\e T Y-m-d H:i:s a A d D F g G h H i j l L m M n O \R\F\C822 r s t U w y Y z Z 2003';\r
+       $s1 = date($fmt,0);\r
+       $s2 = adodb_date($fmt,0);\r
+       if ($s1 != $s2) {\r
+               print " date() 0 failed<br>$s1<br>$s2<br>";\r
+       }\r
+       flush();\r
+       for ($i=100; --$i > 0; ) {\r
+\r
+               $ts = 3600.0*((rand()%60000)+(rand()%60000))+(rand()%60000);\r
+               $s1 = date($fmt,$ts);\r
+               $s2 = adodb_date($fmt,$ts);\r
+               //print "$s1 <br>$s2 <p>";\r
+               $pos = strcmp($s1,$s2);\r
+\r
+               if (($s1) != ($s2)) {\r
+                       for ($j=0,$k=strlen($s1); $j < $k; $j++) {\r
+                               if ($s1[$j] != $s2[$j]) {\r
+                                       print substr($s1,$j).' ';\r
+                                       break;\r
+                               }\r
+                       }\r
+                       print "<b>Error date(): $ts<br><pre> \r
+&nbsp; \"$s1\" (date len=".strlen($s1).")\r
+&nbsp; \"$s2\" (adodb_date len=".strlen($s2).")</b></pre><br>";\r
+                       $fail = true;\r
+               }\r
+               \r
+               $a1 = getdate($ts);\r
+               $a2 = adodb_getdate($ts);\r
+               $rez = array_diff($a1,$a2);\r
+               if (sizeof($rez)>0) {\r
+                       print "<b>Error getdate() $ts</b><br>";\r
+                               print_r($a1);\r
+                       print "<br>";\r
+                               print_r($a2);\r
+                       print "<p>";\r
+                       $fail = true;\r
+               }\r
+       }\r
+       \r
+       // Test generation of dates outside 1901-2038\r
+       print "<p>Testing random dates between 100 and 4000</p>";\r
+       adodb_date_test_date(100,1);\r
+       for ($i=100; --$i >= 0;) {\r
+               $y1 = 100+rand(0,1970-100);\r
+               $m = rand(1,12);\r
+               adodb_date_test_date($y1,$m);\r
+               \r
+               $y1 = 3000-rand(0,3000-1970);\r
+               adodb_date_test_date($y1,$m);\r
+       }\r
+       print '<p>';\r
+       $start = 1960+rand(0,10);\r
+       $yrs = 12;\r
+       $i = 365.25*86400*($start-1970);\r
+       $offset = 36000+rand(10000,60000);\r
+       $max = 365*$yrs*86400;\r
+       $lastyear = 0;\r
+       \r
+       // we generate a timestamp, convert it to a date, and convert it back to a timestamp\r
+       // and check if the roundtrip broke the original timestamp value.\r
+       print "Testing $start to ".($start+$yrs).", or $max seconds, offset=$offset: ";\r
+       $cnt = 0;\r
+       for ($max += $i; $i < $max; $i += $offset) {\r
+               $ret = adodb_date('m,d,Y,H,i,s',$i);\r
+               $arr = explode(',',$ret);\r
+               if ($lastyear != $arr[2]) {\r
+                       $lastyear = $arr[2];\r
+                       print " $lastyear ";\r
+                       flush();\r
+               }\r
+               $newi = adodb_mktime($arr[3],$arr[4],$arr[5],$arr[0],$arr[1],$arr[2]);\r
+               if ($i != $newi) {\r
+                       print "Error at $i, adodb_mktime returned $newi ($ret)";\r
+                       $fail = true;\r
+                       break;\r
+               }\r
+               $cnt += 1;\r
+       }\r
+       echo "Tested $cnt dates<br>";\r
+       if (!$fail) print "<p>Passed !</p>";\r
+       else print "<p><b>Failed</b> :-(</p>";\r
+}\r
+\r
+/**\r
+       Returns day of week, 0 = Sunday,... 6=Saturday. \r
+       Algorithm from PEAR::Date_Calc\r
+*/\r
+function adodb_dow($year, $month, $day)\r
+{\r
+/*\r
+Pope Gregory removed 10 days - October 5 to October 14 - from the year 1582 and \r
+proclaimed that from that time onwards 3 days would be dropped from the calendar \r
+every 400 years.\r
+\r
+Thursday, October 4, 1582 (Julian) was followed immediately by Friday, October 15, 1582 (Gregorian). \r
+*/\r
+       if ($year <= 1582) {\r
+               if ($year < 1582 || \r
+                       ($year == 1582 && ($month < 10 || ($month == 10 && $day < 15)))) $greg_correction = 3;\r
+                else\r
+                       $greg_correction = 0;\r
+       } else\r
+               $greg_correction = 0;\r
+       \r
+       if($month > 2)\r
+           $month -= 2;\r
+       else {\r
+           $month += 10;\r
+           $year--;\r
+       }\r
+       \r
+       $day =  floor((13 * $month - 1) / 5) +\r
+               $day + ($year % 100) +\r
+               floor(($year % 100) / 4) +\r
+               floor(($year / 100) / 4) - 2 *\r
+               floor($year / 100) + 77 + $greg_correction;\r
+       \r
+       return $day - 7 * floor($day / 7);\r
+}\r
+\r
+\r
+/**\r
+ Checks for leap year, returns true if it is. No 2-digit year check. Also \r
+ handles julian calendar correctly.\r
+*/\r
+function _adodb_is_leap_year($year) \r
+{\r
+       if ($year % 4 != 0) return false;\r
+       \r
+       if ($year % 400 == 0) {\r
+               return true;\r
+       // if gregorian calendar (>1582), century not-divisible by 400 is not leap\r
+       } else if ($year > 1582 && $year % 100 == 0 ) {\r
+               return false;\r
+       } \r
+       \r
+       return true;\r
+}\r
+\r
+\r
+/**\r
+ checks for leap year, returns true if it is. Has 2-digit year check\r
+*/\r
+function adodb_is_leap_year($year) \r
+{\r
+       return  _adodb_is_leap_year(adodb_year_digit_check($year));\r
+}\r
+\r
+/**\r
+       Fix 2-digit years. Works for any century.\r
+       Assumes that if 2-digit is more than 30 years in future, then previous century.\r
+*/\r
+function adodb_year_digit_check($y) \r
+{\r
+       if ($y < 100) {\r
+       \r
+               $yr = (integer) date("Y");\r
+               $century = (integer) ($yr /100);\r
+               \r
+               if ($yr%100 > 50) {\r
+                       $c1 = $century + 1;\r
+                       $c0 = $century;\r
+               } else {\r
+                       $c1 = $century;\r
+                       $c0 = $century - 1;\r
+               }\r
+               $c1 *= 100;\r
+               // if 2-digit year is less than 30 years in future, set it to this century\r
+               // otherwise if more than 30 years in future, then we set 2-digit year to the prev century.\r
+               if (($y + $c1) < $yr+30) $y = $y + $c1;\r
+               else $y = $y + $c0*100;\r
+       }\r
+       return $y;\r
+}\r
+\r
+/**\r
+ get local time zone offset from GMT\r
+*/\r
+function adodb_get_gmt_diff() \r
+{\r
+static $TZ;\r
+       if (isset($TZ)) return $TZ;\r
+       \r
+       $TZ = mktime(0,0,0,1,2,1970,0) - gmmktime(0,0,0,1,2,1970,0);\r
+       return $TZ;\r
+}\r
+\r
+/**\r
+       Returns an array with date info.\r
+*/\r
+function adodb_getdate($d=false,$fast=false)\r
+{\r
+       if ($d === false) return getdate();\r
+       if (!defined('ADODB_TEST_DATES')) {\r
+               if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range\r
+                       if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer\r
+                               return @getdate($d);\r
+               }\r
+       }\r
+       return _adodb_getdate($d);\r
+}\r
+\r
+/*\r
+// generate $YRS table for _adodb_getdate()\r
+function adodb_date_gentable($out=true)\r
+{\r
+\r
+       for ($i=1970; $i >= 1600; $i-=10) {\r
+               $s = adodb_gmmktime(0,0,0,1,1,$i);\r
+               echo "$i => $s,<br>";   \r
+       }\r
+}\r
+adodb_date_gentable();\r
+\r
+for ($i=1970; $i > 1500; $i--) {\r
+\r
+echo "<hr>$i ";\r
+       adodb_date_test_date($i,1,1);\r
+}\r
+\r
+*/\r
+\r
+/**\r
+       Low-level function that returns the getdate() array. We have a special\r
+       $fast flag, which if set to true, will return fewer array values,\r
+       and is much faster as it does not calculate dow, etc.\r
+*/\r
+function _adodb_getdate($origd=false,$fast=false,$is_gmt=false)\r
+{\r
+static $YRS;\r
+\r
+       $d =  $origd - ($is_gmt ? 0 : adodb_get_gmt_diff());\r
+       \r
+       $_day_power = 86400;\r
+       $_hour_power = 3600;\r
+       $_min_power = 60;\r
+       \r
+       if ($d < -12219321600) $d -= 86400*10; // if 15 Oct 1582 or earlier, gregorian correction \r
+       \r
+       $_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31);\r
+       $_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31);\r
+       \r
+       $d366 = $_day_power * 366;\r
+       $d365 = $_day_power * 365;\r
+       \r
+       if ($d < 0) {\r
+               \r
+               if (empty($YRS)) $YRS = array(\r
+                       1970 => 0,\r
+                       1960 => -315619200,\r
+                       1950 => -631152000,\r
+                       1940 => -946771200,\r
+                       1930 => -1262304000,\r
+                       1920 => -1577923200,\r
+                       1910 => -1893456000,\r
+                       1900 => -2208988800,\r
+                       1890 => -2524521600,\r
+                       1880 => -2840140800,\r
+                       1870 => -3155673600,\r
+                       1860 => -3471292800,\r
+                       1850 => -3786825600,\r
+                       1840 => -4102444800,\r
+                       1830 => -4417977600,\r
+                       1820 => -4733596800,\r
+                       1810 => -5049129600,\r
+                       1800 => -5364662400,\r
+                       1790 => -5680195200,\r
+                       1780 => -5995814400,\r
+                       1770 => -6311347200,\r
+                       1760 => -6626966400,\r
+                       1750 => -6942499200,\r
+                       1740 => -7258118400,\r
+                       1730 => -7573651200,\r
+                       1720 => -7889270400,\r
+                       1710 => -8204803200,\r
+                       1700 => -8520336000,\r
+                       1690 => -8835868800,\r
+                       1680 => -9151488000,\r
+                       1670 => -9467020800,\r
+                       1660 => -9782640000,\r
+                       1650 => -10098172800,\r
+                       1640 => -10413792000,\r
+                       1630 => -10729324800,\r
+                       1620 => -11044944000,\r
+                       1610 => -11360476800,\r
+                       1600 => -11676096000);\r
+\r
+               if ($is_gmt) $origd = $d;\r
+               // The valid range of a 32bit signed timestamp is typically from \r
+               // Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT\r
+               //\r
+               \r
+               # old algorithm iterates through all years. new algorithm does it in\r
+               # 10 year blocks\r
+               \r
+               /*\r
+               # old algo\r
+               for ($a = 1970 ; --$a >= 0;) {\r
+                       $lastd = $d;\r
+                       \r
+                       if ($leaf = _adodb_is_leap_year($a)) $d += $d366;\r
+                       else $d += $d365;\r
+                       \r
+                       if ($d >= 0) {\r
+                               $year = $a;\r
+                               break;\r
+                       }\r
+               }\r
+               */\r
+               \r
+               $lastsecs = 0;\r
+               $lastyear = 1970;\r
+               foreach($YRS as $year => $secs) {\r
+                       if ($d >= $secs) {\r
+                               $a = $lastyear;\r
+                               break;\r
+                       }\r
+                       $lastsecs = $secs;\r
+                       $lastyear = $year;\r
+               }\r
+               \r
+               $d -= $lastsecs;\r
+               if (!isset($a)) $a = $lastyear;\r
+               \r
+               //echo ' yr=',$a,' ', $d,'.';\r
+               \r
+               for (; --$a >= 0;) {\r
+                       $lastd = $d;\r
+                       \r
+                       if ($leaf = _adodb_is_leap_year($a)) $d += $d366;\r
+                       else $d += $d365;\r
+                       \r
+                       if ($d >= 0) {\r
+                               $year = $a;\r
+                               break;\r
+                       }\r
+               }\r
+               /**/\r
+               \r
+               $secsInYear = 86400 * ($leaf ? 366 : 365) + $lastd;\r
+               \r
+               $d = $lastd;\r
+               $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal;\r
+               for ($a = 13 ; --$a > 0;) {\r
+                       $lastd = $d;\r
+                       $d += $mtab[$a] * $_day_power;\r
+                       if ($d >= 0) {\r
+                               $month = $a;\r
+                               $ndays = $mtab[$a];\r
+                               break;\r
+                       }\r
+               }\r
+               \r
+               $d = $lastd;\r
+               $day = $ndays + ceil(($d+1) / ($_day_power));\r
+\r
+               $d += ($ndays - $day+1)* $_day_power;\r
+               $hour = floor($d/$_hour_power);\r
+       \r
+       } else {\r
+               for ($a = 1970 ;; $a++) {\r
+                       $lastd = $d;\r
+                       \r
+                       if ($leaf = _adodb_is_leap_year($a)) $d -= $d366;\r
+                       else $d -= $d365;\r
+                       if ($d < 0) {\r
+                               $year = $a;\r
+                               break;\r
+                       }\r
+               }\r
+               $secsInYear = $lastd;\r
+               $d = $lastd;\r
+               $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal;\r
+               for ($a = 1 ; $a <= 12; $a++) {\r
+                       $lastd = $d;\r
+                       $d -= $mtab[$a] * $_day_power;\r
+                       if ($d < 0) {\r
+                               $month = $a;\r
+                               $ndays = $mtab[$a];\r
+                               break;\r
+                       }\r
+               }\r
+               $d = $lastd;\r
+               $day = ceil(($d+1) / $_day_power);\r
+               $d = $d - ($day-1) * $_day_power;\r
+               $hour = floor($d /$_hour_power);\r
+       }\r
+       \r
+       $d -= $hour * $_hour_power;\r
+       $min = floor($d/$_min_power);\r
+       $secs = $d - $min * $_min_power;\r
+       if ($fast) {\r
+               return array(\r
+               'seconds' => $secs,\r
+               'minutes' => $min,\r
+               'hours' => $hour,\r
+               'mday' => $day,\r
+               'mon' => $month,\r
+               'year' => $year,\r
+               'yday' => floor($secsInYear/$_day_power),\r
+               'leap' => $leaf,\r
+               'ndays' => $ndays\r
+               );\r
+       }\r
+       \r
+       \r
+       $dow = adodb_dow($year,$month,$day);\r
+\r
+       return array(\r
+               'seconds' => $secs,\r
+               'minutes' => $min,\r
+               'hours' => $hour,\r
+               'mday' => $day,\r
+               'wday' => $dow,\r
+               'mon' => $month,\r
+               'year' => $year,\r
+               'yday' => floor($secsInYear/$_day_power),\r
+               'weekday' => gmdate('l',$_day_power*(3+$dow)),\r
+               'month' => gmdate('F',mktime(0,0,0,$month,2,1971)),\r
+               0 => $origd\r
+       );\r
+}\r
+\r
+function adodb_gmdate($fmt,$d=false)\r
+{\r
+       return adodb_date($fmt,$d,true);\r
+}\r
+\r
+// accepts unix timestamp and iso date format in $d\r
+function adodb_date2($fmt, $d=false, $is_gmt=false)\r
+{\r
+       if ($d !== false) {\r
+               if (!preg_match( \r
+                       "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ -]?(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|", \r
+                       ($d), $rr)) return adodb_date($fmt,false,$is_gmt);\r
+\r
+               if ($rr[1] <= 100 && $rr[2]<= 1) return adodb_date($fmt,false,$is_gmt);\r
+       \r
+               // h-m-s-MM-DD-YY\r
+               if (!isset($rr[5])) $d = adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);\r
+               else $d = @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]);\r
+       }\r
+       \r
+       return adodb_date($fmt,$d,$is_gmt);\r
+}\r
+\r
+\r
+/**\r
+       Return formatted date based on timestamp $d\r
+*/\r
+function adodb_date($fmt,$d=false,$is_gmt=false)\r
+{\r
+static $daylight;\r
+\r
+       if ($d === false) return ($is_gmt)? @gmdate($fmt): @date($fmt);\r
+       if (!defined('ADODB_TEST_DATES')) {\r
+               if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range\r
+                       if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer\r
+                               return ($is_gmt)? @gmdate($fmt,$d): @date($fmt,$d);\r
+\r
+               }\r
+       }\r
+       $_day_power = 86400;\r
+       \r
+       $arr = _adodb_getdate($d,true,$is_gmt);\r
+       \r
+       if (!isset($daylight)) $daylight = function_exists('adodb_daylight_sv');\r
+       if ($daylight) adodb_daylight_sv($arr, $is_gmt);\r
+       \r
+       $year = $arr['year'];\r
+       $month = $arr['mon'];\r
+       $day = $arr['mday'];\r
+       $hour = $arr['hours'];\r
+       $min = $arr['minutes'];\r
+       $secs = $arr['seconds'];\r
+       \r
+       $max = strlen($fmt);\r
+       $dates = '';\r
+       \r
+       /*\r
+               at this point, we have the following integer vars to manipulate:\r
+               $year, $month, $day, $hour, $min, $secs\r
+       */\r
+       for ($i=0; $i < $max; $i++) {\r
+               switch($fmt[$i]) {\r
+               case 'T': $dates .= date('T');break;\r
+               // YEAR\r
+               case 'L': $dates .= $arr['leap'] ? '1' : '0'; break;\r
+               case 'r': // Thu, 21 Dec 2000 16:01:07 +0200\r
+               \r
+                       // 4.3.11 uses '04 Jun 2004'\r
+                       // 4.3.8 uses  ' 4 Jun 2004'\r
+                       $dates .= gmdate('D',$_day_power*(3+adodb_dow($year,$month,$day))).', '         \r
+                               . ($day<10?'0'.$day:$day) . ' '.date('M',mktime(0,0,0,$month,2,1971)).' '.$year.' ';\r
+                       \r
+                       if ($hour < 10) $dates .= '0'.$hour; else $dates .= $hour; \r
+                       \r
+                       if ($min < 10) $dates .= ':0'.$min; else $dates .= ':'.$min;\r
+                       \r
+                       if ($secs < 10) $dates .= ':0'.$secs; else $dates .= ':'.$secs;\r
+                       \r
+                       $gmt = adodb_get_gmt_diff();\r
+                       $dates .= sprintf(' %s%04d',($gmt<0)?'+':'-',abs($gmt)/36); break;\r
+                               \r
+               case 'Y': $dates .= $year; break;\r
+               case 'y': $dates .= substr($year,strlen($year)-2,2); break;\r
+               // MONTH\r
+               case 'm': if ($month<10) $dates .= '0'.$month; else $dates .= $month; break;\r
+               case 'Q': $dates .= ($month+3)>>2; break;\r
+               case 'n': $dates .= $month; break;\r
+               case 'M': $dates .= date('M',mktime(0,0,0,$month,2,1971)); break;\r
+               case 'F': $dates .= date('F',mktime(0,0,0,$month,2,1971)); break;\r
+               // DAY\r
+               case 't': $dates .= $arr['ndays']; break;\r
+               case 'z': $dates .= $arr['yday']; break;\r
+               case 'w': $dates .= adodb_dow($year,$month,$day); break;\r
+               case 'l': $dates .= gmdate('l',$_day_power*(3+adodb_dow($year,$month,$day))); break;\r
+               case 'D': $dates .= gmdate('D',$_day_power*(3+adodb_dow($year,$month,$day))); break;\r
+               case 'j': $dates .= $day; break;\r
+               case 'd': if ($day<10) $dates .= '0'.$day; else $dates .= $day; break;\r
+               case 'S': \r
+                       $d10 = $day % 10;\r
+                       if ($d10 == 1) $dates .= 'st';\r
+                       else if ($d10 == 2 && $day != 12) $dates .= 'nd';\r
+                       else if ($d10 == 3) $dates .= 'rd';\r
+                       else $dates .= 'th';\r
+                       break;\r
+                       \r
+               // HOUR\r
+               case 'Z':\r
+                       $dates .= ($is_gmt) ? 0 : -adodb_get_gmt_diff(); break;\r
+               case 'O': \r
+                       $gmt = ($is_gmt) ? 0 : adodb_get_gmt_diff();\r
+                       $dates .= sprintf('%s%04d',($gmt<0)?'+':'-',abs($gmt)/36); break;\r
+                       \r
+               case 'H': \r
+                       if ($hour < 10) $dates .= '0'.$hour; \r
+                       else $dates .= $hour; \r
+                       break;\r
+               case 'h': \r
+                       if ($hour > 12) $hh = $hour - 12; \r
+                       else {\r
+                               if ($hour == 0) $hh = '12'; \r
+                               else $hh = $hour;\r
+                       }\r
+                       \r
+                       if ($hh < 10) $dates .= '0'.$hh;\r
+                       else $dates .= $hh;\r
+                       break;\r
+                       \r
+               case 'G': \r
+                       $dates .= $hour;\r
+                       break;\r
+                       \r
+               case 'g':\r
+                       if ($hour > 12) $hh = $hour - 12; \r
+                       else {\r
+                               if ($hour == 0) $hh = '12'; \r
+                               else $hh = $hour; \r
+                       }\r
+                       $dates .= $hh;\r
+                       break;\r
+               // MINUTES\r
+               case 'i': if ($min < 10) $dates .= '0'.$min; else $dates .= $min; break;\r
+               // SECONDS\r
+               case 'U': $dates .= $d; break;\r
+               case 's': if ($secs < 10) $dates .= '0'.$secs; else $dates .= $secs; break;\r
+               // AM/PM\r
+               // Note 00:00 to 11:59 is AM, while 12:00 to 23:59 is PM\r
+               case 'a':\r
+                       if ($hour>=12) $dates .= 'pm';\r
+                       else $dates .= 'am';\r
+                       break;\r
+               case 'A':\r
+                       if ($hour>=12) $dates .= 'PM';\r
+                       else $dates .= 'AM';\r
+                       break;\r
+               default:\r
+                       $dates .= $fmt[$i]; break;\r
+               // ESCAPE\r
+               case "\\": \r
+                       $i++;\r
+                       if ($i < $max) $dates .= $fmt[$i];\r
+                       break;\r
+               }\r
+       }\r
+       return $dates;\r
+}\r
+\r
+/**\r
+       Returns a timestamp given a GMT/UTC time. \r
+       Note that $is_dst is not implemented and is ignored.\r
+*/\r
+function adodb_gmmktime($hr,$min,$sec,$mon=false,$day=false,$year=false,$is_dst=false)\r
+{\r
+       return adodb_mktime($hr,$min,$sec,$mon,$day,$year,$is_dst,true);\r
+}\r
+\r
+/**\r
+       Return a timestamp given a local time. Originally by jackbbs.\r
+       Note that $is_dst is not implemented and is ignored.\r
+       \r
+       Not a very fast algorithm - O(n) operation. Could be optimized to O(1).\r
+*/\r
+function adodb_mktime($hr,$min,$sec,$mon=false,$day=false,$year=false,$is_dst=false,$is_gmt=false) \r
+{\r
+       if (!defined('ADODB_TEST_DATES')) {\r
+\r
+               if ($mon === false) {\r
+                       return $is_gmt? @gmmktime($hr,$min,$sec): @mktime($hr,$min,$sec);\r
+               }\r
+               \r
+               // for windows, we don't check 1970 because with timezone differences, \r
+               // 1 Jan 1970 could generate negative timestamp, which is illegal\r
+               if (1971 < $year && $year < 2038\r
+                       || !defined('ADODB_NO_NEGATIVE_TS') && (1901 < $year && $year < 2038)\r
+                       ) {\r
+                               return $is_gmt ?\r
+                                       @gmmktime($hr,$min,$sec,$mon,$day,$year):\r
+                                       @mktime($hr,$min,$sec,$mon,$day,$year);\r
+                       }\r
+       }\r
+       \r
+       $gmt_different = ($is_gmt) ? 0 : adodb_get_gmt_diff();\r
+\r
+       /*\r
+       # disabled because some people place large values in $sec.\r
+       # however we need it for $mon because we use an array...\r
+       $hr = intval($hr);\r
+       $min = intval($min);\r
+       $sec = intval($sec);\r
+       */\r
+       $mon = intval($mon);\r
+       $day = intval($day);\r
+       $year = intval($year);\r
+       \r
+       \r
+       $year = adodb_year_digit_check($year);\r
+\r
+       if ($mon > 12) {\r
+               $y = floor($mon / 12);\r
+               $year += $y;\r
+               $mon -= $y*12;\r
+       } else if ($mon < 1) {\r
+               $y = ceil((1-$mon) / 12);\r
+               $year -= $y;\r
+               $mon += $y*12;\r
+       }\r
+       \r
+       $_day_power = 86400;\r
+       $_hour_power = 3600;\r
+       $_min_power = 60;\r
+       \r
+       $_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31);\r
+       $_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31);\r
+       \r
+       $_total_date = 0;\r
+       if ($year >= 1970) {\r
+               for ($a = 1970 ; $a <= $year; $a++) {\r
+                       $leaf = _adodb_is_leap_year($a);\r
+                       if ($leaf == true) {\r
+                               $loop_table = $_month_table_leaf;\r
+                               $_add_date = 366;\r
+                       } else {\r
+                               $loop_table = $_month_table_normal;\r
+                               $_add_date = 365;\r
+                       }\r
+                       if ($a < $year) { \r
+                               $_total_date += $_add_date;\r
+                       } else {\r
+                               for($b=1;$b<$mon;$b++) {\r
+                                       $_total_date += $loop_table[$b];\r
+                               }\r
+                       }\r
+               }\r
+               $_total_date +=$day-1;\r
+               $ret = $_total_date * $_day_power + $hr * $_hour_power + $min * $_min_power + $sec + $gmt_different;\r
+       \r
+       } else {\r
+               for ($a = 1969 ; $a >= $year; $a--) {\r
+                       $leaf = _adodb_is_leap_year($a);\r
+                       if ($leaf == true) {\r
+                               $loop_table = $_month_table_leaf;\r
+                               $_add_date = 366;\r
+                       } else {\r
+                               $loop_table = $_month_table_normal;\r
+                               $_add_date = 365;\r
+                       }\r
+                       if ($a > $year) { $_total_date += $_add_date;\r
+                       } else {\r
+                               for($b=12;$b>$mon;$b--) {\r
+                                       $_total_date += $loop_table[$b];\r
+                               }\r
+                       }\r
+               }\r
+               $_total_date += $loop_table[$mon] - $day;\r
+               \r
+               $_day_time = $hr * $_hour_power + $min * $_min_power + $sec;\r
+               $_day_time = $_day_power - $_day_time;\r
+               $ret = -( $_total_date * $_day_power + $_day_time - $gmt_different);\r
+               if ($ret < -12220185600) $ret += 10*86400; // if earlier than 5 Oct 1582 - gregorian correction\r
+               else if ($ret < -12219321600) $ret = -12219321600; // if in limbo, reset to 15 Oct 1582.\r
+       } \r
+       //print " dmy=$day/$mon/$year $hr:$min:$sec => " .$ret;\r
+       return $ret;\r
+}\r
+\r
+function adodb_gmstrftime($fmt, $ts=false)\r
+{\r
+       return adodb_strftime($fmt,$ts,true);\r
+}\r
+\r
+// hack - convert to adodb_date\r
+function adodb_strftime($fmt, $ts=false,$is_gmt=false)\r
+{\r
+global $ADODB_DATE_LOCALE;\r
+\r
+       if (!defined('ADODB_TEST_DATES')) {\r
+               if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range\r
+                       if (!defined('ADODB_NO_NEGATIVE_TS') || $ts >= 0) // if windows, must be +ve integer\r
+                               return ($is_gmt)? @gmstrftime($fmt,$ts): @strftime($fmt,$ts);\r
+\r
+               }\r
+       }\r
+       \r
+       if (empty($ADODB_DATE_LOCALE)) {\r
+               $tstr = strtoupper(gmstrftime('%c',31366800)); // 30 Dec 1970, 1 am\r
+               $sep = substr($tstr,2,1);\r
+               $hasAM = strrpos($tstr,'M') !== false;\r
+               \r
+               $ADODB_DATE_LOCALE = array();\r
+               $ADODB_DATE_LOCALE[] =  strncmp($tstr,'30',2) == 0 ? 'd'.$sep.'m'.$sep.'y' : 'm'.$sep.'d'.$sep.'y';     \r
+               $ADODB_DATE_LOCALE[]  = ($hasAM) ? 'h:i:s a' : 'H:i:s';\r
+                       \r
+       }\r
+       $inpct = false;\r
+       $fmtdate = '';\r
+       for ($i=0,$max = strlen($fmt); $i < $max; $i++) {\r
+               $ch = $fmt[$i];\r
+               if ($ch == '%') {\r
+                       if ($inpct) {\r
+                               $fmtdate .= '%';\r
+                               $inpct = false;\r
+                       } else\r
+                               $inpct = true;\r
+               } else if ($inpct) {\r
+               \r
+                       $inpct = false;\r
+                       switch($ch) {\r
+                       case '0':\r
+                       case '1':\r
+                       case '2':\r
+                       case '3':\r
+                       case '4':\r
+                       case '5':\r
+                       case '6':\r
+                       case '7':\r
+                       case '8':\r
+                       case '9':\r
+                       case 'E':\r
+                       case 'O':\r
+                               /* ignore format modifiers */\r
+                               $inpct = true; \r
+                               break;\r
+                               \r
+                       case 'a': $fmtdate .= 'D'; break;\r
+                       case 'A': $fmtdate .= 'l'; break;\r
+                       case 'h':\r
+                       case 'b': $fmtdate .= 'M'; break;\r
+                       case 'B': $fmtdate .= 'F'; break;\r
+                       case 'c': $fmtdate .= $ADODB_DATE_LOCALE[0].$ADODB_DATE_LOCALE[1]; break;\r
+                       case 'C': $fmtdate .= '\C?'; break; // century\r
+                       case 'd': $fmtdate .= 'd'; break;\r
+                       case 'D': $fmtdate .= 'm/d/y'; break;\r
+                       case 'e': $fmtdate .= 'j'; break;\r
+                       case 'g': $fmtdate .= '\g?'; break; //?\r
+                       case 'G': $fmtdate .= '\G?'; break; //?\r
+                       case 'H': $fmtdate .= 'H'; break;\r
+                       case 'I': $fmtdate .= 'h'; break;\r
+                       case 'j': $fmtdate .= '?z'; $parsej = true; break; // wrong as j=1-based, z=0-basd\r
+                       case 'm': $fmtdate .= 'm'; break;\r
+                       case 'M': $fmtdate .= 'i'; break;\r
+                       case 'n': $fmtdate .= "\n"; break;\r
+                       case 'p': $fmtdate .= 'a'; break;\r
+                       case 'r': $fmtdate .= 'h:i:s a'; break;\r
+                       case 'R': $fmtdate .= 'H:i:s'; break;\r
+                       case 'S': $fmtdate .= 's'; break;\r
+                       case 't': $fmtdate .= "\t"; break;\r
+                       case 'T': $fmtdate .= 'H:i:s'; break;\r
+                       case 'u': $fmtdate .= '?u'; $parseu = true; break; // wrong strftime=1-based, date=0-basde\r
+                       case 'U': $fmtdate .= '?U'; $parseU = true; break;// wrong strftime=1-based, date=0-based\r
+                       case 'x': $fmtdate .= $ADODB_DATE_LOCALE[0]; break;\r
+                       case 'X': $fmtdate .= $ADODB_DATE_LOCALE[1]; break;\r
+                       case 'w': $fmtdate .= '?w'; $parseu = true; break; // wrong strftime=1-based, date=0-basde\r
+                       case 'W': $fmtdate .= '?W'; $parseU = true; break;// wrong strftime=1-based, date=0-based\r
+                       case 'y': $fmtdate .= 'y'; break;\r
+                       case 'Y': $fmtdate .= 'Y'; break;\r
+                       case 'Z': $fmtdate .= 'T'; break;\r
+                       }\r
+               } else if (('A' <= ($ch) && ($ch) <= 'Z' ) || ('a' <= ($ch) && ($ch) <= 'z' ))\r
+                       $fmtdate .= "\\".$ch;\r
+               else\r
+                       $fmtdate .= $ch;\r
+       }\r
+       //echo "fmt=",$fmtdate,"<br>";\r
+       if ($ts === false) $ts = time();\r
+       $ret = adodb_date($fmtdate, $ts, $is_gmt);\r
+       return $ret;\r
+}\r
+\r
+\r
+?>
\ No newline at end of file
diff --git a/libraries/adodb/adodb-xmlschema.inc.php b/libraries/adodb/adodb-xmlschema.inc.php
new file mode 100755 (executable)
index 0000000..7b93a65
--- /dev/null
@@ -0,0 +1,2195 @@
+<?php\r
+// Copyright (c) 2004 ars Cognita Inc., all rights reserved\r
+/* ******************************************************************************\r
+    Released under both BSD license and Lesser GPL library license. \r
+       Whenever there is any discrepancy between the two licenses, \r
+       the BSD license will take precedence. \r
+*******************************************************************************/\r
+/**\r
+ * xmlschema is a class that allows the user to quickly and easily\r
+ * build a database on any ADOdb-supported platform using a simple\r
+ * XML schema.\r
+ *\r
+ * Last Editor: $Author: chriskl $\r
+ * @author Richard Tango-Lowy & Dan Cech\r
+ * @version $Revision: 1.1 $\r
+ *\r
+ * @package axmls\r
+ * @tutorial getting_started.pkg\r
+ */\r
+\r
+/**\r
+* Debug on or off\r
+*/\r
+if( !defined( 'XMLS_DEBUG' ) ) {\r
+       define( 'XMLS_DEBUG', FALSE );\r
+}\r
+\r
+/**\r
+* Default prefix key\r
+*/\r
+if( !defined( 'XMLS_PREFIX' ) ) {\r
+       define( 'XMLS_PREFIX', '%%P' );\r
+}\r
+\r
+/**\r
+* Maximum length allowed for object prefix\r
+*/\r
+if( !defined( 'XMLS_PREFIX_MAXLEN' ) ) {\r
+       define( 'XMLS_PREFIX_MAXLEN', 10 );\r
+}\r
+\r
+/**\r
+* Execute SQL inline as it is generated\r
+*/\r
+if( !defined( 'XMLS_EXECUTE_INLINE' ) ) {\r
+       define( 'XMLS_EXECUTE_INLINE', FALSE );\r
+}\r
+\r
+/**\r
+* Continue SQL Execution if an error occurs?\r
+*/\r
+if( !defined( 'XMLS_CONTINUE_ON_ERROR' ) ) {\r
+       define( 'XMLS_CONTINUE_ON_ERROR', FALSE );\r
+}\r
+\r
+/**\r
+* Current Schema Version\r
+*/\r
+if( !defined( 'XMLS_SCHEMA_VERSION' ) ) {\r
+       define( 'XMLS_SCHEMA_VERSION', '0.2' );\r
+}\r
+\r
+/**\r
+* Default Schema Version.  Used for Schemas without an explicit version set.\r
+*/\r
+if( !defined( 'XMLS_DEFAULT_SCHEMA_VERSION' ) ) {\r
+       define( 'XMLS_DEFAULT_SCHEMA_VERSION', '0.1' );\r
+}\r
+\r
+/**\r
+* Default Schema Version.  Used for Schemas without an explicit version set.\r
+*/\r
+if( !defined( 'XMLS_DEFAULT_UPGRADE_METHOD' ) ) {\r
+       define( 'XMLS_DEFAULT_UPGRADE_METHOD', 'ALTER' );\r
+}\r
+\r
+/**\r
+* Include the main ADODB library\r
+*/\r
+if( !defined( '_ADODB_LAYER' ) ) {\r
+       require( 'adodb.inc.php' );\r
+       require( 'adodb-datadict.inc.php' );\r
+}\r
+\r
+/**\r
+* Abstract DB Object. This class provides basic methods for database objects, such\r
+* as tables and indexes.\r
+*\r
+* @package axmls\r
+* @access private\r
+*/\r
+class dbObject {\r
+       \r
+       /**\r
+       * var object Parent\r
+       */\r
+       var $parent;\r
+       \r
+       /**\r
+       * var string current element\r
+       */\r
+       var $currentElement;\r
+       \r
+       /**\r
+       * NOP\r
+       */\r
+       function dbObject( &$parent, $attributes = NULL ) {\r
+               $this->parent =& $parent;\r
+       }\r
+       \r
+       /**\r
+       * XML Callback to process start elements\r
+       *\r
+       * @access private\r
+       */\r
+       function _tag_open( &$parser, $tag, $attributes ) {\r
+               \r
+       }\r
+       \r
+       /**\r
+       * XML Callback to process CDATA elements\r
+       *\r
+       * @access private\r
+       */\r
+       function _tag_cdata( &$parser, $cdata ) {\r
+               \r
+       }\r
+       \r
+       /**\r
+       * XML Callback to process end elements\r
+       *\r
+       * @access private\r
+       */\r
+       function _tag_close( &$parser, $tag ) {\r
+               \r
+       }\r
+       \r
+       function create() {\r
+               return array();\r
+       }\r
+       \r
+       /**\r
+       * Destroys the object\r
+       */\r
+       function destroy() {\r
+               unset( $this );\r
+       }\r
+       \r
+       /**\r
+       * Checks whether the specified RDBMS is supported by the current\r
+       * database object or its ranking ancestor.\r
+       *\r
+       * @param string $platform RDBMS platform name (from ADODB platform list).\r
+       * @return boolean TRUE if RDBMS is supported; otherwise returns FALSE.\r
+       */\r
+       function supportedPlatform( $platform = NULL ) {\r
+               return is_object( $this->parent ) ? $this->parent->supportedPlatform( $platform ) : TRUE;\r
+       }\r
+       \r
+       /**\r
+       * Returns the prefix set by the ranking ancestor of the database object.\r
+       *\r
+       * @param string $name Prefix string.\r
+       * @return string Prefix.\r
+       */\r
+       function prefix( $name = '' ) {\r
+               return is_object( $this->parent ) ? $this->parent->prefix( $name ) : $name;\r
+       }\r
+       \r
+       /**\r
+       * Extracts a field ID from the specified field.\r
+       *\r
+       * @param string $field Field.\r
+       * @return string Field ID.\r
+       */\r
+       function FieldID( $field ) {\r
+               return strtoupper( preg_replace( '/^`(.+)`$/', '$1', $field ) );\r
+       }\r
+}\r
+\r
+/**\r
+* Creates a table object in ADOdb's datadict format\r
+*\r
+* This class stores information about a database table. As charactaristics\r
+* of the table are loaded from the external source, methods and properties\r
+* of this class are used to build up the table description in ADOdb's\r
+* datadict format.\r
+*\r
+* @package axmls\r
+* @access private\r
+*/\r
+class dbTable extends dbObject {\r
+       \r
+       /**\r
+       * @var string Table name\r
+       */\r
+       var $name;\r
+       \r
+       /**\r
+       * @var array Field specifier: Meta-information about each field\r
+       */\r
+       var $fields = array();\r
+       \r
+       /**\r
+       * @var array List of table indexes.\r
+       */\r
+       var $indexes = array();\r
+       \r
+       /**\r
+       * @var array Table options: Table-level options\r
+       */\r
+       var $opts = array();\r
+       \r
+       /**\r
+       * @var string Field index: Keeps track of which field is currently being processed\r
+       */\r
+       var $current_field;\r
+       \r
+       /**\r
+       * @var boolean Mark table for destruction\r
+       * @access private\r
+       */\r
+       var $drop_table;\r
+       \r
+       /**\r
+       * @var boolean Mark field for destruction (not yet implemented)\r
+       * @access private\r
+       */\r
+       var $drop_field = array();\r
+       \r
+       /**\r
+       * Iniitializes a new table object.\r
+       *\r
+       * @param string $prefix DB Object prefix\r
+       * @param array $attributes Array of table attributes.\r
+       */\r
+       function dbTable( &$parent, $attributes = NULL ) {\r
+               $this->parent =& $parent;\r
+               $this->name = $this->prefix($attributes['NAME']);\r
+       }\r
+       \r
+       /**\r
+       * XML Callback to process start elements. Elements currently \r
+       * processed are: INDEX, DROP, FIELD, KEY, NOTNULL, AUTOINCREMENT & DEFAULT. \r
+       *\r
+       * @access private\r
+       */\r
+       function _tag_open( &$parser, $tag, $attributes ) {\r
+               $this->currentElement = strtoupper( $tag );\r
+               \r
+               switch( $this->currentElement ) {\r
+                       case 'INDEX':\r
+                               if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {\r
+                                       xml_set_object( $parser, $this->addIndex( $attributes ) );\r
+                               }\r
+                               break;\r
+                       case 'DATA':\r
+                               if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {\r
+                                       xml_set_object( $parser, $this->addData( $attributes ) );\r
+                               }\r
+                               break;\r
+                       case 'DROP':\r
+                               $this->drop();\r
+                               break;\r
+                       case 'FIELD':\r
+                               // Add a field\r
+                               $fieldName = $attributes['NAME'];\r
+                               $fieldType = $attributes['TYPE'];\r
+                               $fieldSize = isset( $attributes['SIZE'] ) ? $attributes['SIZE'] : NULL;\r
+                               $fieldOpts = isset( $attributes['OPTS'] ) ? $attributes['OPTS'] : NULL;\r
+                               \r
+                               $this->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts );\r
+                               break;\r
+                       case 'KEY':\r
+                       case 'NOTNULL':\r
+                       case 'AUTOINCREMENT':\r
+                               // Add a field option\r
+                               $this->addFieldOpt( $this->current_field, $this->currentElement );\r
+                               break;\r
+                       case 'DEFAULT':\r
+                               // Add a field option to the table object\r
+                               \r
+                               // Work around ADOdb datadict issue that misinterprets empty strings.\r
+                               if( $attributes['VALUE'] == '' ) {\r
+                                       $attributes['VALUE'] = " '' ";\r
+                               }\r
+                               \r
+                               $this->addFieldOpt( $this->current_field, $this->currentElement, $attributes['VALUE'] );\r
+                               break;\r
+                       case 'DEFDATE':\r
+                       case 'DEFTIMESTAMP':\r
+                               // Add a field option to the table object\r
+                               $this->addFieldOpt( $this->current_field, $this->currentElement );\r
+                               break;\r
+                       default:\r
+                               // print_r( array( $tag, $attributes ) );\r
+               }\r
+       }\r
+       \r
+       /**\r
+       * XML Callback to process CDATA elements\r
+       *\r
+       * @access private\r
+       */\r
+       function _tag_cdata( &$parser, $cdata ) {\r
+               switch( $this->currentElement ) {\r
+                       // Table constraint\r
+                       case 'CONSTRAINT':\r
+                               if( isset( $this->current_field ) ) {\r
+                                       $this->addFieldOpt( $this->current_field, $this->currentElement, $cdata );\r
+                               } else {\r
+                                       $this->addTableOpt( $cdata );\r
+                               }\r
+                               break;\r
+                       // Table option\r
+                       case 'OPT':\r
+                               $this->addTableOpt( $cdata );\r
+                               break;\r
+                       default:\r
+                               \r
+               }\r
+       }\r
+       \r
+       /**\r
+       * XML Callback to process end elements\r
+       *\r
+       * @access private\r
+       */\r
+       function _tag_close( &$parser, $tag ) {\r
+               $this->currentElement = '';\r
+               \r
+               switch( strtoupper( $tag ) ) {\r
+                       case 'TABLE':\r
+                               $this->parent->addSQL( $this->create( $this->parent ) );\r
+                               xml_set_object( $parser, $this->parent );\r
+                               $this->destroy();\r
+                               break;\r
+                       case 'FIELD':\r
+                               unset($this->current_field);\r
+                               break;\r
+\r
+               }\r
+       }\r
+       \r
+       /**\r
+       * Adds an index to a table object\r
+       *\r
+       * @param array $attributes Index attributes\r
+       * @return object dbIndex object\r
+       */\r
+       function &addIndex( $attributes ) {\r
+               $name = strtoupper( $attributes['NAME'] );\r
+               $this->indexes[$name] =& new dbIndex( $this, $attributes );\r
+               return $this->indexes[$name];\r
+       }\r
+       \r
+       /**\r
+       * Adds data to a table object\r
+       *\r
+       * @param array $attributes Data attributes\r
+       * @return object dbData object\r
+       */\r
+       function &addData( $attributes ) {\r
+               if( !isset( $this->data ) ) {\r
+                       $this->data =& new dbData( $this, $attributes );\r
+               }\r
+               return $this->data;\r
+       }\r
+       \r
+       /**\r
+       * Adds a field to a table object\r
+       *\r
+       * $name is the name of the table to which the field should be added. \r
+       * $type is an ADODB datadict field type. The following field types\r
+       * are supported as of ADODB 3.40:\r
+       *       - C:  varchar\r
+       *       - X:  CLOB (character large object) or largest varchar size\r
+       *          if CLOB is not supported\r
+       *       - C2: Multibyte varchar\r
+       *       - X2: Multibyte CLOB\r
+       *       - B:  BLOB (binary large object)\r
+       *       - D:  Date (some databases do not support this, and we return a datetime type)\r
+       *       - T:  Datetime or Timestamp\r
+       *       - L:  Integer field suitable for storing booleans (0 or 1)\r
+       *       - I:  Integer (mapped to I4)\r
+       *       - I1: 1-byte integer\r
+       *       - I2: 2-byte integer\r
+       *       - I4: 4-byte integer\r
+       *       - I8: 8-byte integer\r
+       *       - F:  Floating point number\r
+       *       - N:  Numeric or decimal number\r
+       *\r
+       * @param string $name Name of the table to which the field will be added.\r
+       * @param string $type   ADODB datadict field type.\r
+       * @param string $size   Field size\r
+       * @param array $opts    Field options array\r
+       * @return array Field specifier array\r
+       */\r
+       function addField( $name, $type, $size = NULL, $opts = NULL ) {\r
+               $field_id = $this->FieldID( $name );\r
+               \r
+               // Set the field index so we know where we are\r
+               $this->current_field = $field_id;\r
+               \r
+               // Set the field name (required)\r
+               $this->fields[$field_id]['NAME'] = $name;\r
+               \r
+               // Set the field type (required)\r
+               $this->fields[$field_id]['TYPE'] = $type;\r
+               \r
+               // Set the field size (optional)\r
+               if( isset( $size ) ) {\r
+                       $this->fields[$field_id]['SIZE'] = $size;\r
+               }\r
+               \r
+               // Set the field options\r
+               if( isset( $opts ) ) {\r
+                       $this->fields[$field_id]['OPTS'][] = $opts;\r
+               }\r
+       }\r
+       \r
+       /**\r
+       * Adds a field option to the current field specifier\r
+       *\r
+       * This method adds a field option allowed by the ADOdb datadict \r
+       * and appends it to the given field.\r
+       *\r
+       * @param string $field  Field name\r
+       * @param string $opt ADOdb field option\r
+       * @param mixed $value Field option value\r
+       * @return array Field specifier array\r
+       */\r
+       function addFieldOpt( $field, $opt, $value = NULL ) {\r
+               if( !isset( $value ) ) {\r
+                       $this->fields[$this->FieldID( $field )]['OPTS'][] = $opt;\r
+               // Add the option and value\r
+               } else {\r
+                       $this->fields[$this->FieldID( $field )]['OPTS'][] = array( $opt => $value );\r
+               }\r
+       }\r
+       \r
+       /**\r
+       * Adds an option to the table\r
+       *\r
+       * This method takes a comma-separated list of table-level options\r
+       * and appends them to the table object.\r
+       *\r
+       * @param string $opt Table option\r
+       * @return array Options\r
+       */\r
+       function addTableOpt( $opt ) {\r
+               $this->opts[] = $opt;\r
+               \r
+               return $this->opts;\r
+       }\r
+       \r
+       /**\r
+       * Generates the SQL that will create the table in the database\r
+       *\r
+       * @param object $xmls adoSchema object\r
+       * @return array Array containing table creation SQL\r
+       */\r
+       function create( &$xmls ) {\r
+               $sql = array();\r
+               \r
+               // drop any existing indexes\r
+               if( is_array( $legacy_indexes = $xmls->dict->MetaIndexes( $this->name ) ) ) {\r
+                       foreach( $legacy_indexes as $index => $index_details ) {\r
+                               $sql[] = $xmls->dict->DropIndexSQL( $index, $this->name );\r
+                       }\r
+               }\r
+               \r
+               // remove fields to be dropped from table object\r
+               foreach( $this->drop_field as $field ) {\r
+                       unset( $this->fields[$field] );\r
+               }\r
+               \r
+               // if table exists\r
+               if( is_array( $legacy_fields = $xmls->dict->MetaColumns( $this->name ) ) ) {\r
+                       // drop table\r
+                       if( $this->drop_table ) {\r
+                               $sql[] = $xmls->dict->DropTableSQL( $this->name );\r
+                               \r
+                               return $sql;\r
+                       }\r
+                       \r
+                       // drop any existing fields not in schema\r
+                       foreach( $legacy_fields as $field_id => $field ) {\r
+                               if( !isset( $this->fields[$field_id] ) ) {\r
+                                       $sql[] = $xmls->dict->DropColumnSQL( $this->name, '`'.$field->name.'`' );\r
+                               }\r
+                       }\r
+               // if table doesn't exist\r
+               } else {\r
+                       if( $this->drop_table ) {\r
+                               return $sql;\r
+                       }\r
+                       \r
+                       $legacy_fields = array();\r
+               }\r
+               \r
+               // Loop through the field specifier array, building the associative array for the field options\r
+               $fldarray = array();\r
+               \r
+               foreach( $this->fields as $field_id => $finfo ) {\r
+                       // Set an empty size if it isn't supplied\r
+                       if( !isset( $finfo['SIZE'] ) ) {\r
+                               $finfo['SIZE'] = '';\r
+                       }\r
+                       \r
+                       // Initialize the field array with the type and size\r
+                       $fldarray[$field_id] = array(\r
+                               'NAME' => $finfo['NAME'],\r
+                               'TYPE' => $finfo['TYPE'],\r
+                               'SIZE' => $finfo['SIZE']\r
+                       );\r
+                       \r
+                       // Loop through the options array and add the field options. \r
+                       if( isset( $finfo['OPTS'] ) ) {\r
+                               foreach( $finfo['OPTS'] as $opt ) {\r
+                                       // Option has an argument.\r
+                                       if( is_array( $opt ) ) {\r
+                                               $key = key( $opt );\r
+                                               $value = $opt[key( $opt )];\r
+                                               @$fldarray[$field_id][$key] .= $value;\r
+                                       // Option doesn't have arguments\r
+                                       } else {\r
+                                               $fldarray[$field_id][$opt] = $opt;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               if( empty( $legacy_fields ) ) {\r
+                       // Create the new table\r
+                       $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );\r
+                       logMsg( end( $sql ), 'Generated CreateTableSQL' );\r
+               } else {\r
+                       // Upgrade an existing table\r
+                       logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" );\r
+                       switch( $xmls->upgrade ) {\r
+                               // Use ChangeTableSQL\r
+                               case 'ALTER':\r
+                                       logMsg( 'Generated ChangeTableSQL (ALTERing table)' );\r
+                                       $sql[] = $xmls->dict->ChangeTableSQL( $this->name, $fldarray, $this->opts );\r
+                                       break;\r
+                               case 'REPLACE':\r
+                                       logMsg( 'Doing upgrade REPLACE (testing)' );\r
+                                       $sql[] = $xmls->dict->DropTableSQL( $this->name );\r
+                                       $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );\r
+                                       break;\r
+                               // ignore table\r
+                               default:\r
+                                       return array();\r
+                       }\r
+               }\r
+               \r
+               foreach( $this->indexes as $index ) {\r
+                       $sql[] = $index->create( $xmls );\r
+               }\r
+               \r
+               if( isset( $this->data ) ) {\r
+                       $sql[] = $this->data->create( $xmls );\r
+               }\r
+               \r
+               return $sql;\r
+       }\r
+       \r
+       /**\r
+       * Marks a field or table for destruction\r
+       */\r
+       function drop() {\r
+               if( isset( $this->current_field ) ) {\r
+                       // Drop the current field\r
+                       logMsg( "Dropping field '{$this->current_field}' from table '{$this->name}'" );\r
+                       // $this->drop_field[$this->current_field] = $xmls->dict->DropColumnSQL( $this->name, $this->current_field );\r
+                       $this->drop_field[$this->current_field] = $this->current_field;\r
+               } else {\r
+                       // Drop the current table\r
+                       logMsg( "Dropping table '{$this->name}'" );\r
+                       // $this->drop_table = $xmls->dict->DropTableSQL( $this->name );\r
+                       $this->drop_table = TRUE;\r
+               }\r
+       }\r
+}\r
+\r
+/**\r
+* Creates an index object in ADOdb's datadict format\r
+*\r
+* This class stores information about a database index. As charactaristics\r
+* of the index are loaded from the external source, methods and properties\r
+* of this class are used to build up the index description in ADOdb's\r
+* datadict format.\r
+*\r
+* @package axmls\r
+* @access private\r
+*/\r
+class dbIndex extends dbObject {\r
+       \r
+       /**\r
+       * @var string   Index name\r
+       */\r
+       var $name;\r
+       \r
+       /**\r
+       * @var array    Index options: Index-level options\r
+       */\r
+       var $opts = array();\r
+       \r
+       /**\r
+       * @var array    Indexed fields: Table columns included in this index\r
+       */\r
+       var $columns = array();\r
+       \r
+       /**\r
+       * @var boolean Mark index for destruction\r
+       * @access private\r
+       */\r
+       var $drop = FALSE;\r
+       \r
+       /**\r
+       * Initializes the new dbIndex object.\r
+       *\r
+       * @param object $parent Parent object\r
+       * @param array $attributes Attributes\r
+       *\r
+       * @internal\r
+       */\r
+       function dbIndex( &$parent, $attributes = NULL ) {\r
+               $this->parent =& $parent;\r
+               \r
+               $this->name = $this->prefix ($attributes['NAME']);\r
+       }\r
+       \r
+       /**\r
+       * XML Callback to process start elements\r
+       *\r
+       * Processes XML opening tags. \r
+       * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH. \r
+       *\r
+       * @access private\r
+       */\r
+       function _tag_open( &$parser, $tag, $attributes ) {\r
+               $this->currentElement = strtoupper( $tag );\r
+               \r
+               switch( $this->currentElement ) {\r
+                       case 'DROP':\r
+                               $this->drop();\r
+                               break;\r
+                       case 'CLUSTERED':\r
+                       case 'BITMAP':\r
+                       case 'UNIQUE':\r
+                       case 'FULLTEXT':\r
+                       case 'HASH':\r
+                               // Add index Option\r
+                               $this->addIndexOpt( $this->currentElement );\r
+                               break;\r
+                       default:\r
+                               // print_r( array( $tag, $attributes ) );\r
+               }\r
+       }\r
+       \r
+       /**\r
+       * XML Callback to process CDATA elements\r
+       *\r
+       * Processes XML cdata.\r
+       *\r
+       * @access private\r
+       */\r
+       function _tag_cdata( &$parser, $cdata ) {\r
+               switch( $this->currentElement ) {\r
+                       // Index field name\r
+                       case 'COL':\r
+                               $this->addField( $cdata );\r
+                               break;\r
+                       default:\r
+                               \r
+               }\r
+       }\r
+       \r
+       /**\r
+       * XML Callback to process end elements\r
+       *\r
+       * @access private\r
+       */\r
+       function _tag_close( &$parser, $tag ) {\r
+               $this->currentElement = '';\r
+               \r
+               switch( strtoupper( $tag ) ) {\r
+                       case 'INDEX':\r
+                               xml_set_object( $parser, $this->parent );\r
+                               break;\r
+               }\r
+       }\r
+       \r
+       /**\r
+       * Adds a field to the index\r
+       *\r
+       * @param string $name Field name\r
+       * @return string Field list\r
+       */\r
+       function addField( $name ) {\r
+               $this->columns[$this->FieldID( $name )] = $name;\r
+               \r
+               // Return the field list\r
+               return $this->columns;\r
+       }\r
+       \r
+       /**\r
+       * Adds options to the index\r
+       *\r
+       * @param string $opt Comma-separated list of index options.\r
+       * @return string Option list\r
+       */\r
+       function addIndexOpt( $opt ) {\r
+               $this->opts[] = $opt;\r
+               \r
+               // Return the options list\r
+               return $this->opts;\r
+       }\r
+       \r
+       /**\r
+       * Generates the SQL that will create the index in the database\r
+       *\r
+       * @param object $xmls adoSchema object\r
+       * @return array Array containing index creation SQL\r
+       */\r
+       function create( &$xmls ) {\r
+               if( $this->drop ) {\r
+                       return NULL;\r
+               }\r
+               \r
+               // eliminate any columns that aren't in the table\r
+               foreach( $this->columns as $id => $col ) {\r
+                       if( !isset( $this->parent->fields[$id] ) ) {\r
+                               unset( $this->columns[$id] );\r
+                       }\r
+               }\r
+               \r
+               return $xmls->dict->CreateIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts );\r
+       }\r
+       \r
+       /**\r
+       * Marks an index for destruction\r
+       */\r
+       function drop() {\r
+               $this->drop = TRUE;\r
+       }\r
+}\r
+\r
+/**\r
+* Creates a data object in ADOdb's datadict format\r
+*\r
+* This class stores information about table data.\r
+*\r
+* @package axmls\r
+* @access private\r
+*/\r
+class dbData extends dbObject {\r
+       \r
+       var $data = array();\r
+       \r
+       var $row;\r
+       \r
+       /**\r
+       * Initializes the new dbIndex object.\r
+       *\r
+       * @param object $parent Parent object\r
+       * @param array $attributes Attributes\r
+       *\r
+       * @internal\r
+       */\r
+       function dbData( &$parent, $attributes = NULL ) {\r
+               $this->parent =& $parent;\r
+       }\r
+       \r
+       /**\r
+       * XML Callback to process start elements\r
+       *\r
+       * Processes XML opening tags. \r
+       * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH. \r
+       *\r
+       * @access private\r
+       */\r
+       function _tag_open( &$parser, $tag, $attributes ) {\r
+               $this->currentElement = strtoupper( $tag );\r
+               \r
+               switch( $this->currentElement ) {\r
+                       case 'ROW':\r
+                               $this->row = count( $this->data );\r
+                               $this->data[$this->row] = array();\r
+                               break;\r
+                       case 'F':\r
+                               $this->addField($attributes);\r
+                       default:\r
+                               // print_r( array( $tag, $attributes ) );\r
+               }\r
+       }\r
+       \r
+       /**\r
+       * XML Callback to process CDATA elements\r
+       *\r
+       * Processes XML cdata.\r
+       *\r
+       * @access private\r
+       */\r
+       function _tag_cdata( &$parser, $cdata ) {\r
+               switch( $this->currentElement ) {\r
+                       // Index field name\r
+                       case 'F':\r
+                               $this->addData( $cdata );\r
+                               break;\r
+                       default:\r
+                               \r
+               }\r
+       }\r
+       \r
+       /**\r
+       * XML Callback to process end elements\r
+       *\r
+       * @access private\r
+       */\r
+       function _tag_close( &$parser, $tag ) {\r
+               $this->currentElement = '';\r
+               \r
+               switch( strtoupper( $tag ) ) {\r
+                       case 'DATA':\r
+                               xml_set_object( $parser, $this->parent );\r
+                               break;\r
+               }\r
+       }\r
+       \r
+       /**\r
+       * Adds a field to the index\r
+       *\r
+       * @param string $name Field name\r
+       * @return string Field list\r
+       */\r
+       function addField( $attributes ) {\r
+               if( isset( $attributes['NAME'] ) ) {\r
+                       $name = $attributes['NAME'];\r
+               } else {\r
+                       $name = count($this->data[$this->row]);\r
+               }\r
+               \r
+               // Set the field index so we know where we are\r
+               $this->current_field = $this->FieldID( $name );\r
+       }\r
+       \r
+       /**\r
+       * Adds options to the index\r
+       *\r
+       * @param string $opt Comma-separated list of index options.\r
+       * @return string Option list\r
+       */\r
+       function addData( $cdata ) {\r
+               if( !isset( $this->data[$this->row] ) ) {\r
+                       $this->data[$this->row] = array();\r
+               }\r
+               \r
+               if( !isset( $this->data[$this->row][$this->current_field] ) ) {\r
+                       $this->data[$this->row][$this->current_field] = '';\r
+               }\r
+               \r
+               $this->data[$this->row][$this->current_field] .= $cdata;\r
+       }\r
+       \r
+       /**\r
+       * Generates the SQL that will create the index in the database\r
+       *\r
+       * @param object $xmls adoSchema object\r
+       * @return array Array containing index creation SQL\r
+       */\r
+       function create( &$xmls ) {\r
+               $table = $xmls->dict->TableName($this->parent->name);\r
+               $table_field_count = count($this->parent->fields);\r
+               $sql = array();\r
+               \r
+               // eliminate any columns that aren't in the table\r
+               foreach( $this->data as $row ) {\r
+                       $table_fields = $this->parent->fields;\r
+                       $fields = array();\r
+                       \r
+                       foreach( $row as $field_id => $field_data ) {\r
+                               if( !array_key_exists( $field_id, $table_fields ) ) {\r
+                                       if( is_numeric( $field_id ) ) {\r
+                                               $field_id = reset( array_keys( $table_fields ) );\r
+                                       } else {\r
+                                               continue;\r
+                                       }\r
+                               }\r
+                               \r
+                               $name = $table_fields[$field_id]['NAME'];\r
+                               \r
+                               switch( $table_fields[$field_id]['TYPE'] ) {\r
+                                       case 'C':\r
+                                       case 'C2':\r
+                                       case 'X':\r
+                                       case 'X2':\r
+                                               $fields[$name] = $xmls->db->qstr( $field_data );\r
+                                               break;\r
+                                       case 'I':\r
+                                       case 'I1':\r
+                                       case 'I2':\r
+                                       case 'I4':\r
+                                       case 'I8':\r
+                                               $fields[$name] = intval($field_data);\r
+                                               break;\r
+                                       default:\r
+                                               $fields[$name] = $field_data;\r
+                               }\r
+                               \r
+                               unset($table_fields[$field_id]);\r
+                       }\r
+                       \r
+                       // check that at least 1 column is specified\r
+                       if( empty( $fields ) ) {\r
+                               continue;\r
+                       }\r
+                       \r
+                       // check that no required columns are missing\r
+                       if( count( $fields ) < $table_field_count ) {\r
+                               foreach( $table_fields as $field ) {\r
+                                       if (isset( $field['OPTS'] ))\r
+                                               if( ( in_array( 'NOTNULL', $field['OPTS'] ) || in_array( 'KEY', $field['OPTS'] ) ) && !in_array( 'AUTOINCREMENT', $field['OPTS'] ) ) {\r
+                                                       continue(2);\r
+                                               }\r
+                               }\r
+                       }\r
+                       \r
+                       $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')';\r
+               }\r
+               \r
+               return $sql;\r
+       }\r
+}\r
+\r
+/**\r
+* Creates the SQL to execute a list of provided SQL queries\r
+*\r
+* @package axmls\r
+* @access private\r
+*/\r
+class dbQuerySet extends dbObject {\r
+       \r
+       /**\r
+       * @var array    List of SQL queries\r
+       */\r
+       var $queries = array();\r
+       \r
+       /**\r
+       * @var string   String used to build of a query line by line\r
+       */\r
+       var $query;\r
+       \r
+       /**\r
+       * @var string   Query prefix key\r
+       */\r
+       var $prefixKey = '';\r
+       \r
+       /**\r
+       * @var boolean  Auto prefix enable (TRUE)\r
+       */\r
+       var $prefixMethod = 'AUTO';\r
+       \r
+       /**\r
+       * Initializes the query set.\r
+       *\r
+       * @param object $parent Parent object\r
+       * @param array $attributes Attributes\r
+       */\r
+       function dbQuerySet( &$parent, $attributes = NULL ) {\r
+               $this->parent =& $parent;\r
+                       \r
+               // Overrides the manual prefix key\r
+               if( isset( $attributes['KEY'] ) ) {\r
+                       $this->prefixKey = $attributes['KEY'];\r
+               }\r
+               \r
+               $prefixMethod = isset( $attributes['PREFIXMETHOD'] ) ? strtoupper( trim( $attributes['PREFIXMETHOD'] ) ) : '';\r
+               \r
+               // Enables or disables automatic prefix prepending\r
+               switch( $prefixMethod ) {\r
+                       case 'AUTO':\r
+                               $this->prefixMethod = 'AUTO';\r
+                               break;\r
+                       case 'MANUAL':\r
+                               $this->prefixMethod = 'MANUAL';\r
+                               break;\r
+                       case 'NONE':\r
+                               $this->prefixMethod = 'NONE';\r
+                               break;\r
+               }\r
+       }\r
+       \r
+       /**\r
+       * XML Callback to process start elements. Elements currently \r
+       * processed are: QUERY. \r
+       *\r
+       * @access private\r
+       */\r
+       function _tag_open( &$parser, $tag, $attributes ) {\r
+               $this->currentElement = strtoupper( $tag );\r
+               \r
+               switch( $this->currentElement ) {\r
+                       case 'QUERY':\r
+                               // Create a new query in a SQL queryset.\r
+                               // Ignore this query set if a platform is specified and it's different than the \r
+                               // current connection platform.\r
+                               if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {\r
+                                       $this->newQuery();\r
+                               } else {\r
+                                       $this->discardQuery();\r
+                               }\r
+                               break;\r
+                       default:\r
+                               // print_r( array( $tag, $attributes ) );\r
+               }\r
+       }\r
+       \r
+       /**\r
+       * XML Callback to process CDATA elements\r
+       */\r
+       function _tag_cdata( &$parser, $cdata ) {\r
+               switch( $this->currentElement ) {\r
+                       // Line of queryset SQL data\r
+                       case 'QUERY':\r
+                               $this->buildQuery( $cdata );\r
+                               break;\r
+                       default:\r
+                               \r
+               }\r
+       }\r
+       \r
+       /**\r
+       * XML Callback to process end elements\r
+       *\r
+       * @access private\r
+       */\r
+       function _tag_close( &$parser, $tag ) {\r
+               $this->currentElement = '';\r
+               \r
+               switch( strtoupper( $tag ) ) {\r
+                       case 'QUERY':\r
+                               // Add the finished query to the open query set.\r
+                               $this->addQuery();\r
+                               break;\r
+                       case 'SQL':\r
+                               $this->parent->addSQL( $this->create( $this->parent ) );\r
+                               xml_set_object( $parser, $this->parent );\r
+                               $this->destroy();\r
+                               break;\r
+                       default:\r
+                               \r
+               }\r
+       }\r
+       \r
+       /**\r
+       * Re-initializes the query.\r
+       *\r
+       * @return boolean TRUE\r
+       */\r
+       function newQuery() {\r
+               $this->query = '';\r
+               \r
+               return TRUE;\r
+       }\r
+       \r
+       /**\r
+       * Discards the existing query.\r
+       *\r
+       * @return boolean TRUE\r
+       */\r
+       function discardQuery() {\r
+               unset( $this->query );\r
+               \r
+               return TRUE;\r
+       }\r
+       \r
+       /** \r
+       * Appends a line to a query that is being built line by line\r
+       *\r
+       * @param string $data Line of SQL data or NULL to initialize a new query\r
+       * @return string SQL query string.\r
+       */\r
+       function buildQuery( $sql = NULL ) {\r
+               if( !isset( $this->query ) OR empty( $sql ) ) {\r
+                       return FALSE;\r
+               }\r
+               \r
+               $this->query .= $sql;\r
+               \r
+               return $this->query;\r
+       }\r
+       \r
+       /**\r
+       * Adds a completed query to the query list\r
+       *\r
+       * @return string        SQL of added query\r
+       */\r
+       function addQuery() {\r
+               if( !isset( $this->query ) ) {\r
+                       return FALSE;\r
+               }\r
+               \r
+               $this->queries[] = $return = trim($this->query);\r
+               \r
+               unset( $this->query );\r
+               \r
+               return $return;\r
+       }\r
+       \r
+       /**\r
+       * Creates and returns the current query set\r
+       *\r
+       * @param object $xmls adoSchema object\r
+       * @return array Query set\r
+       */\r
+       function create( &$xmls ) {\r
+               foreach( $this->queries as $id => $query ) {\r
+                       switch( $this->prefixMethod ) {\r
+                               case 'AUTO':\r
+                                       // Enable auto prefix replacement\r
+                                       \r
+                                       // Process object prefix.\r
+                                       // Evaluate SQL statements to prepend prefix to objects\r
+                                       $query = $this->prefixQuery( '/^\s*((?is)INSERT\s+(INTO\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );\r
+                                       $query = $this->prefixQuery( '/^\s*((?is)UPDATE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );\r
+                                       $query = $this->prefixQuery( '/^\s*((?is)DELETE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );\r
+                                       \r
+                                       // SELECT statements aren't working yet\r
+                                       #$data = preg_replace( '/(?ias)(^\s*SELECT\s+.*\s+FROM)\s+(\W\s*,?\s*)+((?i)\s+WHERE.*$)/', "\1 $prefix\2 \3", $data );\r
+                                       \r
+                               case 'MANUAL':\r
+                                       // If prefixKey is set and has a value then we use it to override the default constant XMLS_PREFIX.\r
+                                       // If prefixKey is not set, we use the default constant XMLS_PREFIX\r
+                                       if( isset( $this->prefixKey ) AND( $this->prefixKey !== '' ) ) {\r
+                                               // Enable prefix override\r
+                                               $query = str_replace( $this->prefixKey, $xmls->objectPrefix, $query );\r
+                                       } else {\r
+                                               // Use default replacement\r
+                                               $query = str_replace( XMLS_PREFIX , $xmls->objectPrefix, $query );\r
+                                       }\r
+                       }\r
+                       \r
+                       $this->queries[$id] = trim( $query );\r
+               }\r
+               \r
+               // Return the query set array\r
+               return $this->queries;\r
+       }\r
+       \r
+       /**\r
+       * Rebuilds the query with the prefix attached to any objects\r
+       *\r
+       * @param string $regex Regex used to add prefix\r
+       * @param string $query SQL query string\r
+       * @param string $prefix Prefix to be appended to tables, indices, etc.\r
+       * @return string Prefixed SQL query string.\r
+       */\r
+       function prefixQuery( $regex, $query, $prefix = NULL ) {\r
+               if( !isset( $prefix ) ) {\r
+                       return $query;\r
+               }\r
+               \r
+               if( preg_match( $regex, $query, $match ) ) {\r
+                       $preamble = $match[1];\r
+                       $postamble = $match[5];\r
+                       $objectList = explode( ',', $match[3] );\r
+                       // $prefix = $prefix . '_';\r
+                       \r
+                       $prefixedList = '';\r
+                       \r
+                       foreach( $objectList as $object ) {\r
+                               if( $prefixedList !== '' ) {\r
+                                       $prefixedList .= ', ';\r
+                               }\r
+                               \r
+                               $prefixedList .= $prefix . trim( $object );\r
+                       }\r
+                       \r
+                       $query = $preamble . ' ' . $prefixedList . ' ' . $postamble;\r
+               }\r
+               \r
+               return $query;\r
+       }\r
+}\r
+\r
+/**\r
+* Loads and parses an XML file, creating an array of "ready-to-run" SQL statements\r
+* \r
+* This class is used to load and parse the XML file, to create an array of SQL statements\r
+* that can be used to build a database, and to build the database using the SQL array.\r
+*\r
+* @tutorial getting_started.pkg\r
+*\r
+* @author Richard Tango-Lowy & Dan Cech\r
+* @version $Revision: 1.1 $\r
+*\r
+* @package axmls\r
+*/\r
+class adoSchema {\r
+       \r
+       /**\r
+       * @var array    Array containing SQL queries to generate all objects\r
+       * @access private\r
+       */\r
+       var $sqlArray;\r
+       \r
+       /**\r
+       * @var object   ADOdb connection object\r
+       * @access private\r
+       */\r
+       var $db;\r
+       \r
+       /**\r
+       * @var object   ADOdb Data Dictionary\r
+       * @access private\r
+       */\r
+       var $dict;\r
+       \r
+       /**\r
+       * @var string Current XML element\r
+       * @access private\r
+       */\r
+       var $currentElement = '';\r
+       \r
+       /**\r
+       * @var string If set (to 'ALTER' or 'REPLACE'), upgrade an existing database\r
+       * @access private\r
+       */\r
+       var $upgrade = '';\r
+       \r
+       /**\r
+       * @var string Optional object prefix\r
+       * @access private\r
+       */\r
+       var $objectPrefix = '';\r
+       \r
+       /**\r
+       * @var long     Original Magic Quotes Runtime value\r
+       * @access private\r
+       */\r
+       var $mgq;\r
+       \r
+       /**\r
+       * @var long     System debug\r
+       * @access private\r
+       */\r
+       var $debug;\r
+       \r
+       /**\r
+       * @var string Regular expression to find schema version\r
+       * @access private\r
+       */\r
+       var $versionRegex = '/<schema.*?( version="([^"]*)")?.*?>/';\r
+       \r
+       /**\r
+       * @var string Current schema version\r
+       * @access private\r
+       */\r
+       var $schemaVersion;\r
+       \r
+       /**\r
+       * @var int      Success of last Schema execution\r
+       */\r
+       var $success;\r
+       \r
+       /**\r
+       * @var bool     Execute SQL inline as it is generated\r
+       */\r
+       var $executeInline;\r
+       \r
+       /**\r
+       * @var bool     Continue SQL execution if errors occur\r
+       */\r
+       var $continueOnError;\r
+       \r
+       /**\r
+       * Creates an adoSchema object\r
+       *\r
+       * Creating an adoSchema object is the first step in processing an XML schema.\r
+       * The only parameter is an ADOdb database connection object, which must already\r
+       * have been created.\r
+       *\r
+       * @param object $db ADOdb database connection object.\r
+       */\r
+       function adoSchema( &$db ) {\r
+               // Initialize the environment\r
+               $this->mgq = get_magic_quotes_runtime();\r
+               set_magic_quotes_runtime(0);\r
+               \r
+               $this->db =& $db;\r
+               $this->debug = $this->db->debug;\r
+               $this->dict = NewDataDictionary( $this->db );\r
+               $this->sqlArray = array();\r
+               $this->schemaVersion = XMLS_SCHEMA_VERSION;\r
+               $this->executeInline( XMLS_EXECUTE_INLINE );\r
+               $this->continueOnError( XMLS_CONTINUE_ON_ERROR );\r
+               $this->setUpgradeMethod();\r
+       }\r
+       \r
+       /**\r
+       * Sets the method to be used for upgrading an existing database\r
+       *\r
+       * Use this method to specify how existing database objects should be upgraded.\r
+       * The method option can be set to ALTER, REPLACE, BEST, or NONE. ALTER attempts to\r
+       * alter each database object directly, REPLACE attempts to rebuild each object\r
+       * from scratch, BEST attempts to determine the best upgrade method for each\r
+       * object, and NONE disables upgrading.\r
+       *\r
+       * This method is not yet used by AXMLS, but exists for backward compatibility.\r
+       * The ALTER method is automatically assumed when the adoSchema object is\r
+       * instantiated; other upgrade methods are not currently supported.\r
+       *\r
+       * @param string $method Upgrade method (ALTER|REPLACE|BEST|NONE)\r
+       * @returns string Upgrade method used\r
+       */\r
+       function SetUpgradeMethod( $method = '' ) {\r
+               if( !is_string( $method ) ) {\r
+                       return FALSE;\r
+               }\r
+               \r
+               $method = strtoupper( $method );\r
+               \r
+               // Handle the upgrade methods\r
+               switch( $method ) {\r
+                       case 'ALTER':\r
+                               $this->upgrade = $method;\r
+                               break;\r
+                       case 'REPLACE':\r
+                               $this->upgrade = $method;\r
+                               break;\r
+                       case 'BEST':\r
+                               $this->upgrade = 'ALTER';\r
+                               break;\r
+                       case 'NONE':\r
+                               $this->upgrade = 'NONE';\r
+                               break;\r
+                       default:\r
+                               // Use default if no legitimate method is passed.\r
+                               $this->upgrade = XMLS_DEFAULT_UPGRADE_METHOD;\r
+               }\r
+               \r
+               return $this->upgrade;\r
+       }\r
+       \r
+       /**\r
+       * Enables/disables inline SQL execution.\r
+       *\r
+       * Call this method to enable or disable inline execution of the schema. If the mode is set to TRUE (inline execution),\r
+       * AXMLS applies the SQL to the database immediately as each schema entity is parsed. If the mode\r
+       * is set to FALSE (post execution), AXMLS parses the entire schema and you will need to call adoSchema::ExecuteSchema()\r
+       * to apply the schema to the database.\r
+       *\r
+       * @param bool $mode execute\r
+       * @return bool current execution mode\r
+       *\r
+       * @see ParseSchema(), ExecuteSchema()\r
+       */\r
+       function ExecuteInline( $mode = NULL ) {\r
+               if( is_bool( $mode ) ) {\r
+                       $this->executeInline = $mode;\r
+               }\r
+               \r
+               return $this->executeInline;\r
+       }\r
+       \r
+       /**\r
+       * Enables/disables SQL continue on error.\r
+       *\r
+       * Call this method to enable or disable continuation of SQL execution if an error occurs.\r
+       * If the mode is set to TRUE (continue), AXMLS will continue to apply SQL to the database, even if an error occurs.\r
+       * If the mode is set to FALSE (halt), AXMLS will halt execution of generated sql if an error occurs, though parsing\r
+       * of the schema will continue.\r
+       *\r
+       * @param bool $mode execute\r
+       * @return bool current continueOnError mode\r
+       *\r
+       * @see addSQL(), ExecuteSchema()\r
+       */\r
+       function ContinueOnError( $mode = NULL ) {\r
+               if( is_bool( $mode ) ) {\r
+                       $this->continueOnError = $mode;\r
+               }\r
+               \r
+               return $this->continueOnError;\r
+       }\r
+       \r
+       /**\r
+       * Loads an XML schema from a file and converts it to SQL.\r
+       *\r
+       * Call this method to load the specified schema (see the DTD for the proper format) from\r
+       * the filesystem and generate the SQL necessary to create the database described. \r
+       * @see ParseSchemaString()\r
+       *\r
+       * @param string $file Name of XML schema file.\r
+       * @param bool $returnSchema Return schema rather than parsing.\r
+       * @return array Array of SQL queries, ready to execute\r
+       */\r
+       function ParseSchema( $filename, $returnSchema = FALSE ) {\r
+               return $this->ParseSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );\r
+       }\r
+       \r
+       /**\r
+       * Loads an XML schema from a file and converts it to SQL.\r
+       *\r
+       * Call this method to load the specified schema from a file (see the DTD for the proper format) \r
+       * and generate the SQL necessary to create the database described by the schema.\r
+       *\r
+       * @param string $file Name of XML schema file.\r
+       * @param bool $returnSchema Return schema rather than parsing.\r
+       * @return array Array of SQL queries, ready to execute.\r
+       *\r
+       * @deprecated Replaced by adoSchema::ParseSchema() and adoSchema::ParseSchemaString()\r
+       * @see ParseSchema(), ParseSchemaString()\r
+       */\r
+       function ParseSchemaFile( $filename, $returnSchema = FALSE ) {\r
+               // Open the file\r
+               if( !($fp = fopen( $filename, 'r' )) ) {\r
+                       // die( 'Unable to open file' );\r
+                       return FALSE;\r
+               }\r
+               \r
+               // do version detection here\r
+               if( $this->SchemaFileVersion( $filename ) != $this->schemaVersion ) {\r
+                       return FALSE;\r
+               }\r
+               \r
+               if ( $returnSchema )\r
+               {\r
+                       return $xmlstring;\r
+               }\r
+               \r
+               $this->success = 2;\r
+               \r
+               $xmlParser = $this->create_parser();\r
+               \r
+               // Process the file\r
+               while( $data = fread( $fp, 4096 ) ) {\r
+                       if( !xml_parse( $xmlParser, $data, feof( $fp ) ) ) {\r
+                               die( sprintf(\r
+                                       "XML error: %s at line %d",\r
+                                       xml_error_string( xml_get_error_code( $xmlParser) ),\r
+                                       xml_get_current_line_number( $xmlParser)\r
+                               ) );\r
+                       }\r
+               }\r
+               \r
+               xml_parser_free( $xmlParser );\r
+               \r
+               return $this->sqlArray;\r
+       }\r
+       \r
+       /**\r
+       * Converts an XML schema string to SQL.\r
+       *\r
+       * Call this method to parse a string containing an XML schema (see the DTD for the proper format)\r
+       * and generate the SQL necessary to create the database described by the schema. \r
+       * @see ParseSchema()\r
+       *\r
+       * @param string $xmlstring XML schema string.\r
+       * @param bool $returnSchema Return schema rather than parsing.\r
+       * @return array Array of SQL queries, ready to execute.\r
+       */\r
+       function ParseSchemaString( $xmlstring, $returnSchema = FALSE ) {\r
+               if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {\r
+                       return FALSE;\r
+               }\r
+               \r
+               // do version detection here\r
+               if( $this->SchemaStringVersion( $xmlstring ) != $this->schemaVersion ) {\r
+                       return FALSE;\r
+               }\r
+               \r
+               if ( $returnSchema )\r
+               {\r
+                       return $xmlstring;\r
+               }\r
+               \r
+               $this->success = 2;\r
+               \r
+               $xmlParser = $this->create_parser();\r
+               \r
+               if( !xml_parse( $xmlParser, $xmlstring, TRUE ) ) {\r
+                       die( sprintf(\r
+                               "XML error: %s at line %d",\r
+                               xml_error_string( xml_get_error_code( $xmlParser) ),\r
+                               xml_get_current_line_number( $xmlParser)\r
+                       ) );\r
+               }\r
+               \r
+               xml_parser_free( $xmlParser );\r
+               \r
+               return $this->sqlArray;\r
+       }\r
+       \r
+       /**\r
+       * Loads an XML schema from a file and converts it to uninstallation SQL.\r
+       *\r
+       * Call this method to load the specified schema (see the DTD for the proper format) from\r
+       * the filesystem and generate the SQL necessary to remove the database described.\r
+       * @see RemoveSchemaString()\r
+       *\r
+       * @param string $file Name of XML schema file.\r
+       * @param bool $returnSchema Return schema rather than parsing.\r
+       * @return array Array of SQL queries, ready to execute\r
+       */\r
+       function RemoveSchema( $filename, $returnSchema = FALSE ) {\r
+               return $this->RemoveSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );\r
+       }\r
+       \r
+       /**\r
+       * Converts an XML schema string to uninstallation SQL.\r
+       *\r
+       * Call this method to parse a string containing an XML schema (see the DTD for the proper format)\r
+       * and generate the SQL necessary to uninstall the database described by the schema. \r
+       * @see RemoveSchema()\r
+       *\r
+       * @param string $schema XML schema string.\r
+       * @param bool $returnSchema Return schema rather than parsing.\r
+       * @return array Array of SQL queries, ready to execute.\r
+       */\r
+       function RemoveSchemaString( $schema, $returnSchema = FALSE ) {\r
+               \r
+               // grab current version\r
+               if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {\r
+                       return FALSE;\r
+               }\r
+               \r
+               return $this->ParseSchemaString( $this->TransformSchema( $schema, 'remove-' . $version), $returnSchema );\r
+       }\r
+       \r
+       /**\r
+       * Applies the current XML schema to the database (post execution).\r
+       *\r
+       * Call this method to apply the current schema (generally created by calling \r
+       * ParseSchema() or ParseSchemaString() ) to the database (creating the tables, indexes, \r
+       * and executing other SQL specified in the schema) after parsing.\r
+       * @see ParseSchema(), ParseSchemaString(), ExecuteInline()\r
+       *\r
+       * @param array $sqlArray Array of SQL statements that will be applied rather than\r
+       *               the current schema.\r
+       * @param boolean $continueOnErr Continue to apply the schema even if an error occurs.\r
+       * @returns integer 0 if failure, 1 if errors, 2 if successful.\r
+       */\r
+       function ExecuteSchema( $sqlArray = NULL, $continueOnErr =  NULL ) {\r
+               if( !is_bool( $continueOnErr ) ) {\r
+                       $continueOnErr = $this->ContinueOnError();\r
+               }\r
+               \r
+               if( !isset( $sqlArray ) ) {\r
+                       $sqlArray = $this->sqlArray;\r
+               }\r
+               \r
+               if( !is_array( $sqlArray ) ) {\r
+                       $this->success = 0;\r
+               } else {\r
+                       $this->success = $this->dict->ExecuteSQLArray( $sqlArray, $continueOnErr );\r
+               }\r
+               \r
+               return $this->success;\r
+       }\r
+       \r
+       /**\r
+       * Returns the current SQL array. \r
+       *\r
+       * Call this method to fetch the array of SQL queries resulting from \r
+       * ParseSchema() or ParseSchemaString(). \r
+       *\r
+       * @param string $format Format: HTML, TEXT, or NONE (PHP array)\r
+       * @return array Array of SQL statements or FALSE if an error occurs\r
+       */\r
+       function PrintSQL( $format = 'NONE' ) {\r
+               return $this->getSQL( $format, $sqlArray );\r
+       }\r
+       \r
+       /**\r
+       * Saves the current SQL array to the local filesystem as a list of SQL queries.\r
+       *\r
+       * Call this method to save the array of SQL queries (generally resulting from a\r
+       * parsed XML schema) to the filesystem.\r
+       *\r
+       * @param string $filename Path and name where the file should be saved.\r
+       * @return boolean TRUE if save is successful, else FALSE. \r
+       */\r
+       function SaveSQL( $filename = './schema.sql' ) {\r
+               \r
+               if( !isset( $sqlArray ) ) {\r
+                       $sqlArray = $this->sqlArray;\r
+               }\r
+               if( !isset( $sqlArray ) ) {\r
+                       return FALSE;\r
+               }\r
+               \r
+               $fp = fopen( $filename, "w" );\r
+               \r
+               foreach( $sqlArray as $key => $query ) {\r
+                       fwrite( $fp, $query . ";\n" );\r
+               }\r
+               fclose( $fp );\r
+       }\r
+       \r
+       /**\r
+       * Create an xml parser\r
+       *\r
+       * @return object PHP XML parser object\r
+       *\r
+       * @access private\r
+       */\r
+       function &create_parser() {\r
+               // Create the parser\r
+               $xmlParser = xml_parser_create();\r
+               xml_set_object( $xmlParser, $this );\r
+               \r
+               // Initialize the XML callback functions\r
+               xml_set_element_handler( $xmlParser, '_tag_open', '_tag_close' );\r
+               xml_set_character_data_handler( $xmlParser, '_tag_cdata' );\r
+               \r
+               return $xmlParser;\r
+       }\r
+       \r
+       /**\r
+       * XML Callback to process start elements\r
+       *\r
+       * @access private\r
+       */\r
+       function _tag_open( &$parser, $tag, $attributes ) {\r
+               switch( strtoupper( $tag ) ) {\r
+                       case 'TABLE':\r
+                               $this->obj = new dbTable( $this, $attributes );\r
+                               xml_set_object( $parser, $this->obj );\r
+                               break;\r
+                       case 'SQL':\r
+                               if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {\r
+                                       $this->obj = new dbQuerySet( $this, $attributes );\r
+                                       xml_set_object( $parser, $this->obj );\r
+                               }\r
+                               break;\r
+                       default:\r
+                               // print_r( array( $tag, $attributes ) );\r
+               }\r
+               \r
+       }\r
+       \r
+       /**\r
+       * XML Callback to process CDATA elements\r
+       *\r
+       * @access private\r
+       */\r
+       function _tag_cdata( &$parser, $cdata ) {\r
+       }\r
+       \r
+       /**\r
+       * XML Callback to process end elements\r
+       *\r
+       * @access private\r
+       * @internal\r
+       */\r
+       function _tag_close( &$parser, $tag ) {\r
+               \r
+       }\r
+       \r
+       /**\r
+       * Converts an XML schema string to the specified DTD version.\r
+       *\r
+       * Call this method to convert a string containing an XML schema to a different AXMLS\r
+       * DTD version. For instance, to convert a schema created for an pre-1.0 version for \r
+       * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version \r
+       * parameter is specified, the schema will be converted to the current DTD version. \r
+       * If the newFile parameter is provided, the converted schema will be written to the specified\r
+       * file.\r
+       * @see ConvertSchemaFile()\r
+       *\r
+       * @param string $schema String containing XML schema that will be converted.\r
+       * @param string $newVersion DTD version to convert to.\r
+       * @param string $newFile File name of (converted) output file.\r
+       * @return string Converted XML schema or FALSE if an error occurs.\r
+       */\r
+       function ConvertSchemaString( $schema, $newVersion = NULL, $newFile = NULL ) {\r
+               \r
+               // grab current version\r
+               if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {\r
+                       return FALSE;\r
+               }\r
+               \r
+               if( !isset ($newVersion) ) {\r
+                       $newVersion = $this->schemaVersion;\r
+               }\r
+               \r
+               if( $version == $newVersion ) {\r
+                       $result = $schema;\r
+               } else {\r
+                       $result = $this->TransformSchema( $schema, 'convert-' . $version . '-' . $newVersion);\r
+               }\r
+               \r
+               if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {\r
+                       fwrite( $fp, $result );\r
+                       fclose( $fp );\r
+               }\r
+               \r
+               return $result;\r
+       }\r
+       \r
+       /**\r
+       * Converts an XML schema file to the specified DTD version.\r
+       *\r
+       * Call this method to convert the specified XML schema file to a different AXMLS\r
+       * DTD version. For instance, to convert a schema created for an pre-1.0 version for \r
+       * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version \r
+       * parameter is specified, the schema will be converted to the current DTD version. \r
+       * If the newFile parameter is provided, the converted schema will be written to the specified\r
+       * file.\r
+       * @see ConvertSchemaString()\r
+       *\r
+       * @param string $filename Name of XML schema file that will be converted.\r
+       * @param string $newVersion DTD version to convert to.\r
+       * @param string $newFile File name of (converted) output file.\r
+       * @return string Converted XML schema or FALSE if an error occurs.\r
+       */\r
+       function ConvertSchemaFile( $filename, $newVersion = NULL, $newFile = NULL ) {\r
+               \r
+               // grab current version\r
+               if( !( $version = $this->SchemaFileVersion( $filename ) ) ) {\r
+                       return FALSE;\r
+               }\r
+               \r
+               if( !isset ($newVersion) ) {\r
+                       $newVersion = $this->schemaVersion;\r
+               }\r
+               \r
+               if( $version == $newVersion ) {\r
+                       $result = file_get_contents( $filename );\r
+                       \r
+                       // remove unicode BOM if present\r
+                       if( substr( $result, 0, 3 ) == sprintf( '%c%c%c', 239, 187, 191 ) ) {\r
+                               $result = substr( $result, 3 );\r
+                       }\r
+               } else {\r
+                       $result = $this->TransformSchema( $filename, 'convert-' . $version . '-' . $newVersion, 'file' );\r
+               }\r
+               \r
+               if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {\r
+                       fwrite( $fp, $result );\r
+                       fclose( $fp );\r
+               }\r
+               \r
+               return $result;\r
+       }\r
+       \r
+       function TransformSchema( $schema, $xsl, $schematype='string' )\r
+       {\r
+               // Fail if XSLT extension is not available\r
+               if( ! function_exists( 'xslt_create' ) ) {\r
+                       return FALSE;\r
+               }\r
+               \r
+               $xsl_file = dirname( __FILE__ ) . '/xsl/' . $xsl . '.xsl';\r
+               \r
+               // look for xsl\r
+               if( !is_readable( $xsl_file ) ) {\r
+                       return FALSE;\r
+               }\r
+               \r
+               switch( $schematype )\r
+               {\r
+                       case 'file':\r
+                               if( !is_readable( $schema ) ) {\r
+                                       return FALSE;\r
+                               }\r
+                               \r
+                               $schema = file_get_contents( $schema );\r
+                               break;\r
+                       case 'string':\r
+                       default:\r
+                               if( !is_string( $schema ) ) {\r
+                                       return FALSE;\r
+                               }\r
+               }\r
+               \r
+               $arguments = array (\r
+                       '/_xml' => $schema,\r
+                       '/_xsl' => file_get_contents( $xsl_file )\r
+               );\r
+               \r
+               // create an XSLT processor\r
+               $xh = xslt_create ();\r
+               \r
+               // set error handler\r
+               xslt_set_error_handler ($xh, array (&$this, 'xslt_error_handler'));\r
+               \r
+               // process the schema\r
+               $result = xslt_process ($xh, 'arg:/_xml', 'arg:/_xsl', NULL, $arguments); \r
+               \r
+               xslt_free ($xh);\r
+               \r
+               return $result;\r
+       }\r
+       \r
+       /**\r
+       * Processes XSLT transformation errors\r
+       *\r
+       * @param object $parser XML parser object\r
+       * @param integer $errno Error number\r
+       * @param integer $level Error level\r
+       * @param array $fields Error information fields\r
+       *\r
+       * @access private\r
+       */\r
+       function xslt_error_handler( $parser, $errno, $level, $fields ) {\r
+               if( is_array( $fields ) ) {\r
+                       $msg = array(\r
+                               'Message Type' => ucfirst( $fields['msgtype'] ),\r
+                               'Message Code' => $fields['code'],\r
+                               'Message' => $fields['msg'],\r
+                               'Error Number' => $errno,\r
+                               'Level' => $level\r
+                       );\r
+                       \r
+                       switch( $fields['URI'] ) {\r
+                               case 'arg:/_xml':\r
+                                       $msg['Input'] = 'XML';\r
+                                       break;\r
+                               case 'arg:/_xsl':\r
+                                       $msg['Input'] = 'XSL';\r
+                                       break;\r
+                               default:\r
+                                       $msg['Input'] = $fields['URI'];\r
+                       }\r
+                       \r
+                       $msg['Line'] = $fields['line'];\r
+               } else {\r
+                       $msg = array(\r
+                               'Message Type' => 'Error',\r
+                               'Error Number' => $errno,\r
+                               'Level' => $level,\r
+                               'Fields' => var_export( $fields, TRUE )\r
+                       );\r
+               }\r
+               \r
+               $error_details = $msg['Message Type'] . ' in XSLT Transformation' . "\n"\r
+                                          . '<table>' . "\n";\r
+               \r
+               foreach( $msg as $label => $details ) {\r
+                       $error_details .= '<tr><td><b>' . $label . ': </b></td><td>' . htmlentities( $details ) . '</td></tr>' . "\n";\r
+               }\r
+               \r
+               $error_details .= '</table>';\r
+               \r
+               trigger_error( $error_details, E_USER_ERROR );\r
+       }\r
+       \r
+       /**\r
+       * Returns the AXMLS Schema Version of the requested XML schema file.\r
+       *\r
+       * Call this method to obtain the AXMLS DTD version of the requested XML schema file.\r
+       * @see SchemaStringVersion()\r
+       *\r
+       * @param string $filename AXMLS schema file\r
+       * @return string Schema version number or FALSE on error\r
+       */\r
+       function SchemaFileVersion( $filename ) {\r
+               // Open the file\r
+               if( !($fp = fopen( $filename, 'r' )) ) {\r
+                       // die( 'Unable to open file' );\r
+                       return FALSE;\r
+               }\r
+               \r
+               // Process the file\r
+               while( $data = fread( $fp, 4096 ) ) {\r
+                       if( preg_match( $this->versionRegex, $data, $matches ) ) {\r
+                               return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;\r
+                       }\r
+               }\r
+               \r
+               return FALSE;\r
+       }\r
+       \r
+       /**\r
+       * Returns the AXMLS Schema Version of the provided XML schema string.\r
+       *\r
+       * Call this method to obtain the AXMLS DTD version of the provided XML schema string.\r
+       * @see SchemaFileVersion()\r
+       *\r
+       * @param string $xmlstring XML schema string\r
+       * @return string Schema version number or FALSE on error\r
+       */\r
+       function SchemaStringVersion( $xmlstring ) {\r
+               if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {\r
+                       return FALSE;\r
+               }\r
+               \r
+               if( preg_match( $this->versionRegex, $xmlstring, $matches ) ) {\r
+                       return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;\r
+               }\r
+               \r
+               return FALSE;\r
+       }\r
+       \r
+       /**\r
+       * Extracts an XML schema from an existing database.\r
+       *\r
+       * Call this method to create an XML schema string from an existing database.\r
+       * If the data parameter is set to TRUE, AXMLS will include the data from the database\r
+       * in the schema. \r
+       *\r
+       * @param boolean $data Include data in schema dump\r
+       * @return string Generated XML schema\r
+       */\r
+       function ExtractSchema( $data = FALSE ) {\r
+               $old_mode = $this->db->SetFetchMode( ADODB_FETCH_NUM );\r
+               \r
+               $schema = '<?xml version="1.0"?>' . "\n"\r
+                               . '<schema version="' . $this->schemaVersion . '">' . "\n";\r
+               \r
+               if( is_array( $tables = $this->db->MetaTables( 'TABLES' ) ) ) {\r
+                       foreach( $tables as $table ) {\r
+                               $schema .= '    <table name="' . $table . '">' . "\n";\r
+                               \r
+                               // grab details from database\r
+                               $rs = $this->db->Execute( 'SELECT * FROM ' . $table . ' WHERE 1=1' );\r
+                               $fields = $this->db->MetaColumns( $table );\r
+                               $indexes = $this->db->MetaIndexes( $table );\r
+                               \r
+                               if( is_array( $fields ) ) {\r
+                                       foreach( $fields as $details ) {\r
+                                               $extra = '';\r
+                                               $content = array();\r
+                                               \r
+                                               if( $details->max_length > 0 ) {\r
+                                                       $extra .= ' size="' . $details->max_length . '"';\r
+                                               }\r
+                                               \r
+                                               if( $details->primary_key ) {\r
+                                                       $content[] = '<KEY/>';\r
+                                               } elseif( $details->not_null ) {\r
+                                                       $content[] = '<NOTNULL/>';\r
+                                               }\r
+                                               \r
+                                               if( $details->has_default ) {\r
+                                                       $content[] = '<DEFAULT value="' . $details->default_value . '"/>';\r
+                                               }\r
+                                               \r
+                                               if( $details->auto_increment ) {\r
+                                                       $content[] = '<AUTOINCREMENT/>';\r
+                                               }\r
+                                               \r
+                                               // this stops the creation of 'R' columns,\r
+                                               // AUTOINCREMENT is used to create auto columns\r
+                                               $details->primary_key = 0;\r
+                                               $type = $rs->MetaType( $details );\r
+                                               \r
+                                               $schema .= '            <field name="' . $details->name . '" type="' . $type . '"' . $extra . '>';\r
+