--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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)." <code>$ss</code>\n<br>\n",false);
+ else
+ ADOConnection::outp( "<hr>\n($dbt): ".htmlspecialchars($sqlTxt)." <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) ? ' ' : "\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
--- /dev/null
+<?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>|<</code>';\r
+ var $prev = '<code><<</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> \r
+ <?php\r
+ } else {\r
+ print "$this->first ";\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> \r
+ <?php\r
+ } else {\r
+ print "$this->next ";\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> \r
+ <?php\r
+ } else {\r
+ print "$this->last ";\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 . ' ';\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> \r
+ <?php \r
+ } else {\r
+ print "$this->prev ";\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 = " ";\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
--- /dev/null
+<?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
--- /dev/null
+<?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]).' '.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> </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> <a href=?do=viewsql><b>View SQL</b></a>\r
+ <a href=?do=tables><b>View Tables</b></a> <a href=?do=poll><b>Poll Stats</b></a>",\r
+ $allowsql ? ' <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 " <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> </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 = ' ';\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 = ' ';\r
+ if (strlen($val)==0) $val = ' ';\r
+ if ($cli) {\r
+ $html .= str_replace(' ','',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=" < " name="SMALLER"><input type="submit" value=" > > " 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> </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 ' '.$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
--- /dev/null
+<?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
--- /dev/null
+<?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
+ \"$s1\" (date len=".strlen($s1).")\r
+ \"$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
--- /dev/null
+<?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
+