]> git.mjollnir.org Git - moodle.git/commitdiff
MDL-19055 Upgrade to AdoDB 5.08 + local patches reapplied
authorstronk7 <stronk7>
Sun, 3 May 2009 22:33:18 +0000 (22:33 +0000)
committerstronk7 <stronk7>
Sun, 3 May 2009 22:33:18 +0000 (22:33 +0000)
97 files changed:
lib/adodb/adodb-active-record.inc.php
lib/adodb/adodb-active-recordx.inc.php [new file with mode: 0644]
lib/adodb/adodb-csvlib.inc.php
lib/adodb/adodb-datadict.inc.php
lib/adodb/adodb-error.inc.php
lib/adodb/adodb-errorhandler.inc.php
lib/adodb/adodb-errorpear.inc.php
lib/adodb/adodb-exceptions.inc.php
lib/adodb/adodb-iterator.inc.php
lib/adodb/adodb-lib.inc.php
lib/adodb/adodb-memcache.lib.inc.php
lib/adodb/adodb-pager.inc.php
lib/adodb/adodb-pear.inc.php
lib/adodb/adodb-perf.inc.php
lib/adodb/adodb-php4.inc.php
lib/adodb/adodb-time.inc.php
lib/adodb/adodb-xmlschema.inc.php
lib/adodb/adodb-xmlschema03.inc.php
lib/adodb/adodb.inc.php
lib/adodb/datadict/datadict-access.inc.php
lib/adodb/datadict/datadict-db2.inc.php
lib/adodb/datadict/datadict-firebird.inc.php
lib/adodb/datadict/datadict-generic.inc.php
lib/adodb/datadict/datadict-ibase.inc.php
lib/adodb/datadict/datadict-informix.inc.php
lib/adodb/datadict/datadict-mssql.inc.php
lib/adodb/datadict/datadict-mssqlnative.inc.php [new file with mode: 0644]
lib/adodb/datadict/datadict-mysql.inc.php
lib/adodb/datadict/datadict-oci8.inc.php
lib/adodb/datadict/datadict-postgres.inc.php
lib/adodb/datadict/datadict-sapdb.inc.php
lib/adodb/datadict/datadict-sybase.inc.php
lib/adodb/drivers/adodb-access.inc.php
lib/adodb/drivers/adodb-ado.inc.php
lib/adodb/drivers/adodb-ado5.inc.php
lib/adodb/drivers/adodb-ado_access.inc.php
lib/adodb/drivers/adodb-ado_mssql.inc.php
lib/adodb/drivers/adodb-borland_ibase.inc.php
lib/adodb/drivers/adodb-csv.inc.php
lib/adodb/drivers/adodb-db2.inc.php
lib/adodb/drivers/adodb-fbsql.inc.php
lib/adodb/drivers/adodb-firebird.inc.php
lib/adodb/drivers/adodb-ibase.inc.php
lib/adodb/drivers/adodb-informix.inc.php
lib/adodb/drivers/adodb-informix72.inc.php
lib/adodb/drivers/adodb-ldap.inc.php
lib/adodb/drivers/adodb-mssql.inc.php
lib/adodb/drivers/adodb-mssql_n.inc.php
lib/adodb/drivers/adodb-mssqlnative.inc.php [new file with mode: 0644]
lib/adodb/drivers/adodb-mssqlpo.inc.php
lib/adodb/drivers/adodb-mysql.inc.php
lib/adodb/drivers/adodb-mysqli.inc.php
lib/adodb/drivers/adodb-mysqlpo.inc.php
lib/adodb/drivers/adodb-mysqlt.inc.php
lib/adodb/drivers/adodb-netezza.inc.php
lib/adodb/drivers/adodb-oci8.inc.php
lib/adodb/drivers/adodb-oci8.old.inc.php [new file with mode: 0644]
lib/adodb/drivers/adodb-oci805.inc.php
lib/adodb/drivers/adodb-oci8po.inc.php
lib/adodb/drivers/adodb-odbc.inc.php
lib/adodb/drivers/adodb-odbc_db2.inc.php
lib/adodb/drivers/adodb-odbc_mssql.inc.php
lib/adodb/drivers/adodb-odbc_oracle.inc.php
lib/adodb/drivers/adodb-odbtp.inc.php
lib/adodb/drivers/adodb-odbtp_unicode.inc.php
lib/adodb/drivers/adodb-oracle.inc.php
lib/adodb/drivers/adodb-pdo.inc.php
lib/adodb/drivers/adodb-pdo_mssql.inc.php
lib/adodb/drivers/adodb-pdo_mysql.inc.php
lib/adodb/drivers/adodb-pdo_oci.inc.php
lib/adodb/drivers/adodb-pdo_pgsql.inc.php
lib/adodb/drivers/adodb-pdo_sqlite.inc.php [new file with mode: 0644]
lib/adodb/drivers/adodb-postgres.inc.php
lib/adodb/drivers/adodb-postgres64.inc.php
lib/adodb/drivers/adodb-postgres7.inc.php
lib/adodb/drivers/adodb-postgres8.inc.php
lib/adodb/drivers/adodb-proxy.inc.php
lib/adodb/drivers/adodb-sapdb.inc.php
lib/adodb/drivers/adodb-sqlanywhere.inc.php
lib/adodb/drivers/adodb-sqlite.inc.php
lib/adodb/drivers/adodb-sqlitepo.inc.php
lib/adodb/drivers/adodb-sybase.inc.php
lib/adodb/drivers/adodb-sybase_ase.inc.php
lib/adodb/drivers/adodb-vfp.inc.php
lib/adodb/lang/adodb-fa.inc.php [new file with mode: 0644]
lib/adodb/perf/perf-db2.inc.php
lib/adodb/perf/perf-informix.inc.php
lib/adodb/perf/perf-mssql.inc.php
lib/adodb/perf/perf-mssqlnative.inc.php [new file with mode: 0644]
lib/adodb/perf/perf-mysql.inc.php
lib/adodb/perf/perf-oci8.inc.php
lib/adodb/perf/perf-postgres.inc.php
lib/adodb/pivottable.inc.php
lib/adodb/readme_moodle.txt
lib/adodb/rsfilter.inc.php
lib/adodb/toexport.inc.php
lib/adodb/tohtml.inc.php

index 328eaa8b63322e41bcf0d1c19c327a2f361fc4d2..796713b06e2fefba3611ba7c405644a510f89a07 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
 
-@version V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+@version V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). All rights reserved.
   Latest version is available at http://adodb.sourceforge.net
  
   Released under both BSD license and Lesser GPL library license. 
   
   Active Record implementation. Superset of Zend Framework's.
   
-  Version 0.08
+  Version 0.92
   
   See http://www-128.ibm.com/developerworks/java/library/j-cb03076/?ca=dgr-lnxw01ActiveRecord 
        for info on Ruby on Rails Active Record implementation
 */
 
+
 global $_ADODB_ACTIVE_DBS;
 global $ADODB_ACTIVE_CACHESECS; // set to true to enable caching of metadata such as field info
 global $ACTIVE_RECORD_SAFETY; // set to false to disable safety checks
@@ -36,10 +37,15 @@ class ADODB_Active_Table {
        var $flds; // assoc array of adofieldobjs, indexed by fieldname
        var $keys; // assoc array of primary keys, indexed by fieldname
        var $_created; // only used when stored as a cached file
+       var $_belongsTo = array();
+       var $_hasMany = array();
 }
 
+// $db = database connection
+// $index = name of index - can be associative, for an example see
+//    http://phplens.com/lens/lensforum/msgs.php?id=17790 
 // returns index into $_ADODB_ACTIVE_DBS
-function ADODB_SetDatabaseAdapter(&$db)
+function ADODB_SetDatabaseAdapter(&$db, $index=false)
 {
        global $_ADODB_ACTIVE_DBS;
        
@@ -56,13 +62,18 @@ function ADODB_SetDatabaseAdapter(&$db)
                $obj->db = $db;
                $obj->tables = array();
                
-               $_ADODB_ACTIVE_DBS[] = $obj;
+               if ($index == false) $index = sizeof($_ADODB_ACTIVE_DBS);
+
+               
+               $_ADODB_ACTIVE_DBS[$index] = $obj;
                
                return sizeof($_ADODB_ACTIVE_DBS)-1;
 }
 
 
 class ADODB_Active_Record {
+       static $_changeNames = true; // dynamically pluralize table names
+       static $_foreignSuffix = '_id'; // 
        var $_dbat; // associative index pointing to ADODB_Active_DB eg. $ADODB_Active_DBS[_dbat]
        var $_table; // tablename, if set in class definition then use it as table name
        var $_tableat; // associative index pointing to ADODB_Active_Table, eg $ADODB_Active_DBS[_dbat]->tables[$this->_tableat]
@@ -70,7 +81,9 @@ class ADODB_Active_Record {
        var $_saved = false; // indicates whether data is already inserted.
        var $_lasterr = false; // last error message
        var $_original = false; // the original values loaded or inserted, refreshed on update
-       
+
+       var $foreignName; // CFR: class name when in a relationship
+
        static function UseDefaultValues($bool=null)
        {
        global $ADODB_ACTIVE_DEFVALS;
@@ -79,9 +92,9 @@ class ADODB_Active_Record {
        }
 
        // should be static
-       static function SetDatabaseAdapter(&$db) 
+       static function SetDatabaseAdapter(&$db, $index=false
        {
-               return ADODB_SetDatabaseAdapter($db);
+               return ADODB_SetDatabaseAdapter($db, $index);
        }
        
        
@@ -105,16 +118,18 @@ class ADODB_Active_Record {
                        if (!empty($this->_table)) $table = $this->_table;
                        else $table = $this->_pluralize(get_class($this));
                }
+               $this->foreignName = strtolower(get_class($this)); // CFR: default foreign name
                if ($db) {
                        $this->_dbat = ADODB_Active_Record::SetDatabaseAdapter($db);
-               } else
-                       $this->_dbat = sizeof($_ADODB_ACTIVE_DBS)-1;
-               
-               
-               if ($this->_dbat < 0) $this->Error("No database connection set; use ADOdb_Active_Record::SetDatabaseAdapter(\$db)",'ADODB_Active_Record::__constructor');
-               
+               } else if (!isset($this->_dbat)) {
+                       if (sizeof($_ADODB_ACTIVE_DBS) == 0) $this->Error("No database connection set; use ADOdb_Active_Record::SetDatabaseAdapter(\$db)",'ADODB_Active_Record::__constructor');
+                       end($_ADODB_ACTIVE_DBS);
+                       $this->_dbat = key($_ADODB_ACTIVE_DBS);
+               }
+
                $this->_table = $table;
                $this->_tableat = $table; # reserved for setting the assoc value to a non-table name, eg. the sql string in future
+
                $this->UpdateActiveTable($pkeyarr);
        }
        
@@ -126,6 +141,8 @@ class ADODB_Active_Record {
        
        function _pluralize($table)
        {
+               if (!ADODB_Active_Record::$_changeNames) return $table;
+
                $ut = strtoupper($table);
                $len = strlen($table);
                $lastc = $ut[$len-1];
@@ -145,13 +162,178 @@ class ADODB_Active_Record {
                }
        }
        
+       // CFR Lamest singular inflector ever - @todo Make it real!
+       // Note: There is an assumption here...and it is that the argument's length >= 4
+       function _singularize($tables)
+       {
+       
+               if (!ADODB_Active_Record::$_changeNames) return $table;
+       
+               $ut = strtoupper($tables);
+               $len = strlen($tables);
+               if($ut[$len-1] != 'S')
+                       return $tables; // I know...forget oxen
+               if($ut[$len-2] != 'E')
+                       return substr($tables, 0, $len-1);
+               switch($ut[$len-3])
+               {
+                       case 'S':
+                       case 'X':
+                               return substr($tables, 0, $len-2);
+                       case 'I':
+                               return substr($tables, 0, $len-3) . 'y';
+                       case 'H';
+                               if($ut[$len-4] == 'C' || $ut[$len-4] == 'S')
+                                       return substr($tables, 0, $len-2);
+                       default:
+                               return substr($tables, 0, $len-1); // ?
+               }
+       }
+
+       function hasMany($foreignRef, $foreignKey = false, $foreignClass = 'ADODB_Active_Record')
+       {
+               $ar = new $foreignClass($foreignRef);
+               $ar->foreignName = $foreignRef;
+               $ar->UpdateActiveTable();
+               $ar->foreignKey = ($foreignKey) ? $foreignKey : $foreignRef.ADODB_Active_Record::$_foreignSuffix;
+               $table =& $this->TableInfo();
+               $table->_hasMany[$foreignRef] = $ar;
+       #       $this->$foreignRef = $this->_hasMany[$foreignRef]; // WATCHME Removed assignment by ref. to please __get()
+       }
+       
+       // use when you don't want ADOdb to auto-pluralize tablename
+       static function TableHasMany($table, $foreignRef, $foreignKey = false, $foreignClass = 'ADODB_Active_Record')
+       {
+               $ar = new ADODB_Active_Record($table);
+               $ar->hasMany($foreignRef, $foreignKey, $foreignClass);
+       }
+       
+       // use when you don't want ADOdb to auto-pluralize tablename
+       static function TableKeyHasMany($table, $tablePKey, $foreignRef, $foreignKey = false, $foreignClass = 'ADODB_Active_Record')
+       {
+               if (!is_array($tablePKey)) $tablePKey = array($tablePKey);
+               $ar = new ADODB_Active_Record($table,$tablePKey);
+               $ar->hasMany($foreignRef, $foreignKey, $foreignClass);
+       }
+       
+       
+       // use when you want ADOdb to auto-pluralize tablename for you. Note that the class must already be defined.
+       // e.g. class Person will generate relationship for table Persons
+       static function ClassHasMany($parentclass, $foreignRef, $foreignKey = false, $foreignClass = 'ADODB_Active_Record')
+       {
+               $ar = new $parentclass();
+               $ar->hasMany($foreignRef, $foreignKey, $foreignClass);
+       }
+       
+
+       function belongsTo($foreignRef,$foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record')
+       {
+               global $inflector;
+
+               $ar = new $parentClass($this->_pluralize($foreignRef));
+               $ar->foreignName = $foreignRef;
+               $ar->parentKey = $parentKey;
+               $ar->UpdateActiveTable();
+               $ar->foreignKey = ($foreignKey) ? $foreignKey : $foreignRef.ADODB_Active_Record::$_foreignSuffix;
+               
+               $table =& $this->TableInfo();
+               $table->_belongsTo[$foreignRef] = $ar;
+       #       $this->$foreignRef = $this->_belongsTo[$foreignRef];
+       }
+       
+       static function ClassBelongsTo($class, $foreignRef, $foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record')
+       {
+               $ar = new $class();
+               $ar->belongsTo($foreignRef, $foreignKey, $parentKey, $parentClass);
+       }
+       
+       static function TableBelongsTo($table, $foreignRef, $foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record')
+       {
+               $ar = new ADOdb_Active_Record($table);
+               $ar->belongsTo($foreignRef, $foreignKey, $parentKey, $parentClass);
+       }
+       
+       static function TableKeyBelongsTo($table, $tablePKey, $foreignRef, $foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record')
+       {
+               if (!is_array($tablePKey)) $tablePKey = array($tablePKey);
+               $ar = new ADOdb_Active_Record($table, $tablePKey);
+               $ar->belongsTo($foreignRef, $foreignKey, $parentKey, $parentClass);
+       }
+
+
+       /**
+        * __get Access properties - used for lazy loading
+        * 
+        * @param mixed $name 
+        * @access protected
+        * @return mixed
+        */
+        function __get($name)
+       {
+               return $this->LoadRelations($name, '', -1, -1);
+       }
+       
+       /**
+        * @param string $name 
+        * @param string $whereOrderBy : eg. ' AND field1 = value ORDER BY field2'
+        * @param offset
+        * @param limit
+        * @return mixed
+        */
+       function LoadRelations($name, $whereOrderBy='', $offset=-1,$limit=-1)
+       {
+               $extras = array();
+               $table = $this->TableInfo();
+               if ($limit >= 0) $extras['limit'] = $limit;
+               if ($offset >= 0) $extras['offset'] = $offset;
+               
+               if (strlen($whereOrderBy)) 
+                       if (!preg_match('/^[ \n\r]*AND/i',$whereOrderBy))
+                               if (!preg_match('/^[ \n\r]*ORDER[ \n\r]/i',$whereOrderBy))
+                                       $whereOrderBy = 'AND '.$whereOrderBy;
+                               
+               if(!empty($table->_belongsTo[$name]))
+               {
+                       $obj = $table->_belongsTo[$name];
+                       $columnName = $obj->foreignKey;
+                       if(empty($this->$columnName))
+                               $this->$name = null;
+                       else
+                       {
+                               if ($obj->parentKey) $key = $obj->parentKey;
+                               else $key = reset($table->keys);
+                               
+                               $arrayOfOne = $obj->Find($key.'='.$this->$columnName.' '.$whereOrderBy,false,false,$extras);
+                               if ($arrayOfOne) {
+                                       $this->$name = $arrayOfOne[0];
+                                       return $arrayOfOne[0];
+                               }
+                       }
+               }
+               if(!empty($table->_hasMany[$name]))
+               {       
+                       $obj = $table->_hasMany[$name];
+                       $key = reset($table->keys);
+                       $id = @$this->$key;
+                       if (!is_numeric($id)) {
+                               $db = $this->DB();
+                               $id = $db->qstr($id);
+                       }
+                       $objs = $obj->Find($obj->foreignKey.'='.$id. ' '.$whereOrderBy,false,false,$extras);
+                       if (!$objs) $objs = array();
+                       $this->$name = $objs;
+                       return $objs;
+               }
+               
+               return array();
+       }
        //////////////////////////////////
        
        // update metadata
        function UpdateActiveTable($pkeys=false,$forceUpdate=false)
        {
        global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS , $ADODB_CACHE_DIR, $ADODB_ACTIVE_CACHESECS;
-       global $ADODB_ACTIVE_DEFVALS;
+       global $ADODB_ACTIVE_DEFVALS,$ADODB_FETCH_MODE;
 
                $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
 
@@ -169,7 +351,6 @@ class ADODB_Active_Record {
                        }
                        return;
                }
-               
                $db = $activedb->db;
                $fname = $ADODB_CACHE_DIR . '/adodb_' . $db->databaseType . '_active_'. $table . '.cache';
                if (!$forceUpdate && $ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR && file_exists($fname)) {
@@ -191,8 +372,15 @@ class ADODB_Active_Record {
                $activetab = new ADODB_Active_Table();
                $activetab->name = $table;
                
+               $save = $ADODB_FETCH_MODE;
+               $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
+               if ($db->fetchMode !== false) $savem = $db->SetFetchMode(false);
                
                $cols = $db->MetaColumns($table);
+               
+               if (isset($savem)) $db->SetFetchMode($savem);
+               $ADODB_FETCH_MODE = $save;
+               
                if (!$cols) {
                        $this->Error("Invalid table name: $table",'UpdateActiveTable'); 
                        return false;
@@ -270,6 +458,12 @@ class ADODB_Active_Record {
                        if (!function_exists('adodb_write_file')) include(ADODB_DIR.'/adodb-csvlib.inc.php');
                        adodb_write_file($fname,$s);
                }
+               if (isset($activedb->tables[$table])) {
+                       $oldtab = $activedb->tables[$table];
+               
+                       if ($oldtab) $activetab->_belongsTo = $oldtab->_belongsTo;
+                       if ($oldtab) $activetab->_hasMany = $oldtab->_hasMany;
+               }
                $activedb->tables[$table] = $activetab;
        }
        
@@ -338,15 +532,26 @@ class ADODB_Active_Record {
        }
        
        // retrieve ADODB_Active_Table
-       function TableInfo()
+       function &TableInfo()
        {
        global $_ADODB_ACTIVE_DBS;
-       
                $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
                $table = $activedb->tables[$this->_tableat];
                return $table;
        }
        
+       
+       // I have an ON INSERT trigger on a table that sets other columns in the table.
+       // So, I find that for myTable, I want to reload an active record after saving it. -- Malcolm Cook
+       function Reload()
+       {
+               $db =& $this->DB(); if (!$db) return false;
+               $table =& $this->TableInfo();
+               $where = $this->GenWhere($db, $table);
+               return($this->Load($where));
+       }
+
+       
        // set a numeric array (using natural table field ordering) as object properties
        function Set(&$row)
        {
@@ -378,7 +583,8 @@ class ADODB_Active_Record {
             # </AP>
                }
         else
-               $keys = array_keys($row);
+                       $keys = array_keys($row);
+                       
         # <AP>
         reset($keys);
         $this->_original = array();
@@ -388,6 +594,7 @@ class ADODB_Active_Record {
             $this->_original[] = $value;
             next($keys);
                }
+
         # </AP>
                return true;
        }
@@ -414,12 +621,15 @@ class ADODB_Active_Record {
                case 'D':
                case 'T':
                        if (empty($val)) return 'null';
-                       
+               
+               case 'B':
+               case 'N':
                case 'C':
                case 'X':
                        if (is_null($val)) return 'null';
                        
-                       if (strncmp($val,"'",1) != 0 && substr($val,strlen($val)-1,1) != "'") { 
+                       if (strlen($val)>1 && 
+                               (strncmp($val,"'",1) != 0 || substr($val,strlen($val)-1,1) != "'")) { 
                                return $db->qstr($val);
                                break;
                        }
@@ -447,18 +657,48 @@ class ADODB_Active_Record {
        
        //------------------------------------------------------------ Public functions below
        
-       function Load($where,$bindarr=false)
+       function Load($where=null,$bindarr=false)
        {
+       global $ADODB_FETCH_MODE;
+       
                $db = $this->DB(); if (!$db) return false;
                $this->_where = $where;
                
-               $save = $db->SetFetchMode(ADODB_FETCH_NUM);
-               $row = $db->GetRow("select * from ".$this->_table.' WHERE '.$where,$bindarr);
-               $db->SetFetchMode($save);
+               $save = $ADODB_FETCH_MODE;
+               $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+               if ($db->fetchMode !== false) $savem = $db->SetFetchMode(false);
+               
+               $qry = "select * from ".$this->_table;
+
+               if($where) {
+                       $qry .= ' WHERE '.$where;
+               }
+               $row = $db->GetRow($qry,$bindarr);
+               
+               if (isset($savem)) $db->SetFetchMode($savem);
+               $ADODB_FETCH_MODE = $save;
                
                return $this->Set($row);
        }
        
+       # useful for multiple record inserts
+       # see http://phplens.com/lens/lensforum/msgs.php?id=17795
+       function Reset()
+       {
+        $this->_where=null;
+        $this->_saved = false; 
+        $this->_lasterr = false; 
+        $this->_original = false;
+        $vars=get_object_vars($this);
+        foreach($vars as $k=>$v){
+               if(substr($k,0,1)!=='_'){
+                       $this->{$k}=null;
+               }
+        }
+        $this->foreignName=strtolower(get_class($this));
+        return true;
+    }
+       
        // false on error
        function Save()
        {
@@ -481,7 +721,7 @@ class ADODB_Active_Record {
 
                foreach($table->flds as $name=>$fld) {
                        $val = $this->$name;
-                       if(!is_null($val) || !array_key_exists($name, $table->keys)) {
+                       if(!is_array($val) || !is_null($val) || !array_key_exists($name, $table->keys)) {
                                $valarr[] = $val;
                                $names[] = $name;
                                $valstr[] = $db->Param($cnt);
@@ -532,10 +772,10 @@ class ADODB_Active_Record {
        }
        
        // returns an array of active record objects
-       function Find($whereOrderBy,$bindarr=false,$pkeysArr=false)
+       function Find($whereOrderBy,$bindarr=false,$pkeysArr=false,$extra=array())
        {
                $db = $this->DB(); if (!$db || empty($this->_table)) return false;
-               $arr = $db->GetActiveRecordsClass(get_class($this),$this->_table, $whereOrderBy,$bindarr,$pkeysArr);
+               $arr = $db->GetActiveRecordsClass(get_class($this),$this->_table, $whereOrderBy,$bindarr,$pkeysArr,$extra);
                return $arr;
        }
        
@@ -564,6 +804,9 @@ class ADODB_Active_Record {
                        if (is_null($val) && !empty($fld->auto_increment)) {
                continue;
             }
+                       
+                       if (is_array($val)) continue;
+                       
                        $t = $db->MetaType($fld->type);
                        $arr[$name] = $this->doquote($db,$val,$t);
                        $valarr[] = $val;
@@ -623,9 +866,8 @@ class ADODB_Active_Record {
                        $val = $this->$name;
                        $neworig[] = $val;
                        
-                       if (isset($table->keys[$name])) {
+                       if (isset($table->keys[$name]) || is_array($val)) 
                                continue;
-                       }
                        
                        if (is_null($val)) {
                                if (isset($fld->not_null) && $fld->not_null) {
@@ -665,4 +907,65 @@ class ADODB_Active_Record {
        
 };
 
+function adodb_GetActiveRecordsClass(&$db, $class, $table,$whereOrderBy,$bindarr, $primkeyArr,
+                       $extra)
+{
+global $_ADODB_ACTIVE_DBS;
+
+       
+       $save = $db->SetFetchMode(ADODB_FETCH_NUM);
+       $qry = "select * from ".$table;
+       
+       if (!empty($whereOrderBy))
+               $qry .= ' WHERE '.$whereOrderBy;
+       if(isset($extra['limit']))
+       {
+               $rows = false;
+               if(isset($extra['offset'])) {
+                       $rs = $db->SelectLimit($qry, $extra['limit'], $extra['offset']);
+               } else {
+                       $rs = $db->SelectLimit($qry, $extra['limit']);
+               }
+               if ($rs) {
+                       while (!$rs->EOF) {
+                               $rows[] = $rs->fields;
+                               $rs->MoveNext();
+                       }
+               }
+       } else
+               $rows = $db->GetAll($qry,$bindarr);
+
+       $db->SetFetchMode($save);
+       
+       $false = false;
+       
+       if ($rows === false) {  
+               return $false;
+       }
+       
+
+       if (!class_exists($class)) {
+               $db->outp_throw("Unknown class $class in GetActiveRecordsClass()",'GetActiveRecordsClass');
+               return $false;
+       }
+       $arr = array();
+       // arrRef will be the structure that knows about our objects.
+       // It is an associative array.
+       // We will, however, return arr, preserving regular 0.. order so that
+       // obj[0] can be used by app developpers.
+       $arrRef = array();
+       $bTos = array(); // Will store belongTo's indices if any
+       foreach($rows as $row) {
+       
+               $obj = new $class($table,$primkeyArr,$db);
+               if ($obj->ErrorNo()){
+                       $db->_errorMsg = $obj->ErrorMsg();
+                       return $false;
+               }
+               $obj->Set($row);
+               $arr[] = $obj;
+       } // foreach($rows as $row) 
+
+       return $arr;
+}
 ?>
\ No newline at end of file
diff --git a/lib/adodb/adodb-active-recordx.inc.php b/lib/adodb/adodb-active-recordx.inc.php
new file mode 100644 (file)
index 0000000..6d270b6
--- /dev/null
@@ -0,0 +1,1421 @@
+<?php
+/*
+
+@version V5.06 29 Sept 2008   (c) 2000-2009 John Lim (jlim#natsoft.com). All rights reserved.
+  Latest version is available at http://adodb.sourceforge.net
+  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.
+  
+  Active Record implementation. Superset of Zend Framework's.
+  
+  This is "Active Record eXtended" to support JOIN, WORK and LAZY mode by Chris Ravenscroft  chris#voilaweb.com 
+  
+  Version 0.9
+  
+  See http://www-128.ibm.com/developerworks/java/library/j-cb03076/?ca=dgr-lnxw01ActiveRecord 
+       for info on Ruby on Rails Active Record implementation
+*/
+
+
+       // CFR: Active Records Definitions
+define('ADODB_JOIN_AR', 0x01);
+define('ADODB_WORK_AR', 0x02);
+define('ADODB_LAZY_AR', 0x03);
+
+
+global $_ADODB_ACTIVE_DBS;
+global $ADODB_ACTIVE_CACHESECS; // set to true to enable caching of metadata such as field info
+global $ACTIVE_RECORD_SAFETY; // set to false to disable safety checks
+global $ADODB_ACTIVE_DEFVALS; // use default values of table definition when creating new active record.
+
+// array of ADODB_Active_DB's, indexed by ADODB_Active_Record->_dbat
+$_ADODB_ACTIVE_DBS = array();
+$ACTIVE_RECORD_SAFETY = true; // CFR: disabled while playing with relations
+$ADODB_ACTIVE_DEFVALS = false;
+
+class ADODB_Active_DB {
+       var $db; // ADOConnection
+       var $tables; // assoc array of ADODB_Active_Table objects, indexed by tablename
+}
+
+class ADODB_Active_Table {
+       var $name; // table name
+       var $flds; // assoc array of adofieldobjs, indexed by fieldname
+       var $keys; // assoc array of primary keys, indexed by fieldname
+       var $_created; // only used when stored as a cached file
+       var $_belongsTo = array();
+       var $_hasMany = array();
+       var $_colsCount; // total columns count, including relations
+
+       function updateColsCount()
+       {
+               $this->_colsCount = sizeof($this->flds);
+               foreach($this->_belongsTo as $foreignTable)
+                       $this->_colsCount += sizeof($foreignTable->TableInfo()->flds);
+               foreach($this->_hasMany as $foreignTable)
+                       $this->_colsCount += sizeof($foreignTable->TableInfo()->flds);
+       }
+}
+
+// returns index into $_ADODB_ACTIVE_DBS
+function ADODB_SetDatabaseAdapter(&$db)
+{
+       global $_ADODB_ACTIVE_DBS;
+       
+               foreach($_ADODB_ACTIVE_DBS as $k => $d) {
+                       if (PHP_VERSION >= 5) {
+                               if ($d->db === $db) return $k;
+                       } else {
+                               if ($d->db->_connectionID === $db->_connectionID && $db->database == $d->db->database) 
+                                       return $k;
+                       }
+               }
+               
+               $obj = new ADODB_Active_DB();
+               $obj->db = $db;
+               $obj->tables = array();
+               
+               $_ADODB_ACTIVE_DBS[] = $obj;
+               
+               return sizeof($_ADODB_ACTIVE_DBS)-1;
+}
+
+
+class ADODB_Active_Record {
+       static $_changeNames = true; // dynamically pluralize table names
+       static $_foreignSuffix = '_id'; // 
+       var $_dbat; // associative index pointing to ADODB_Active_DB eg. $ADODB_Active_DBS[_dbat]
+       var $_table; // tablename, if set in class definition then use it as table name
+       var $_sTable; // singularized table name
+       var $_pTable; // pluralized table name
+       var $_tableat; // associative index pointing to ADODB_Active_Table, eg $ADODB_Active_DBS[_dbat]->tables[$this->_tableat]
+       var $_where; // where clause set in Load()
+       var $_saved = false; // indicates whether data is already inserted.
+       var $_lasterr = false; // last error message
+       var $_original = false; // the original values loaded or inserted, refreshed on update
+
+       var $foreignName; // CFR: class name when in a relationship
+
+       static function UseDefaultValues($bool=null)
+       {
+       global $ADODB_ACTIVE_DEFVALS;
+               if (isset($bool)) $ADODB_ACTIVE_DEFVALS = $bool;
+               return $ADODB_ACTIVE_DEFVALS;
+       }
+
+       // should be static
+       static function SetDatabaseAdapter(&$db) 
+       {
+               return ADODB_SetDatabaseAdapter($db);
+       }
+       
+       
+       public function __set($name, $value)
+       {
+               $name = str_replace(' ', '_', $name);
+               $this->$name = $value;
+       }
+       
+       // php5 constructor
+       // Note: if $table is defined, then we will use it as our table name
+       // Otherwise we will use our classname...
+       // In our database, table names are pluralized (because there can be
+       // more than one row!)
+       // Similarly, if $table is defined here, it has to be plural form.
+       //
+       // $options is an array that allows us to tweak the constructor's behaviour
+       // if $options['refresh'] is true, we re-scan our metadata information
+       // if $options['new'] is true, we forget all relations
+       function __construct($table = false, $pkeyarr=false, $db=false, $options=array())
+       {
+       global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS;
+       
+               if ($db == false && is_object($pkeyarr)) {
+                       $db = $pkeyarr;
+                       $pkeyarr = false;
+               }
+               
+               if($table)
+               {
+                       // table argument exists. It is expected to be
+                       // already plural form.
+                       $this->_pTable = $table;
+                       $this->_sTable = $this->_singularize($this->_pTable);
+               }
+               else
+               {
+                       // We will use current classname as table name.
+                       // We need to pluralize it for the real table name.
+                       $this->_sTable = strtolower(get_class($this));
+                       $this->_pTable = $this->_pluralize($this->_sTable);
+               }
+               $this->_table = &$this->_pTable;
+
+               $this->foreignName = $this->_sTable; // CFR: default foreign name (singular)
+
+               if ($db) {
+                       $this->_dbat = ADODB_Active_Record::SetDatabaseAdapter($db);
+               } else
+                       $this->_dbat = sizeof($_ADODB_ACTIVE_DBS)-1;
+               
+               
+               if ($this->_dbat < 0) $this->Error("No database connection set; use ADOdb_Active_Record::SetDatabaseAdapter(\$db)",'ADODB_Active_Record::__constructor');
+               
+               $this->_tableat = $this->_table; # reserved for setting the assoc value to a non-table name, eg. the sql string in future
+
+               // CFR: Just added this option because UpdateActiveTable() can refresh its information
+               // but there was no way to ask it to do that.
+               $forceUpdate = (isset($options['refresh']) && true === $options['refresh']);
+               $this->UpdateActiveTable($pkeyarr, $forceUpdate);
+               if(isset($options['new']) && true === $options['new'])
+               {
+                       $table =& $this->TableInfo();
+                       unset($table->_hasMany);
+                       unset($table->_belongsTo);
+                       $table->_hasMany = array();
+                       $table->_belongsTo = array();
+               }
+       }
+       
+       function __wakeup()
+       {
+               $class = get_class($this);
+               new $class;
+       }
+       
+       // CFR: Constants found in Rails
+       static $IrregularP = array(
+               'PERSON'    => 'people',
+               'MAN'       => 'men',
+               'WOMAN'     => 'women',
+               'CHILD'     => 'children',
+               'COW'       => 'kine',
+       );
+
+       static $IrregularS = array(
+               'PEOPLE'    => 'PERSON',
+               'MEN'       => 'man',
+               'WOMEN'     => 'woman',
+               'CHILDREN'  => 'child',
+               'KINE'      => 'cow',
+       );
+
+       static $WeIsI = array(
+               'EQUIPMENT' => true,
+               'INFORMATION'   => true,
+               'RICE'      => true,
+               'MONEY'     => true,
+               'SPECIES'   => true,
+               'SERIES'    => true,
+               'FISH'      => true,
+               'SHEEP'     => true,
+       );
+
+       function _pluralize($table)
+       {
+               if (!ADODB_Active_Record::$_changeNames) return $table;
+
+               $ut = strtoupper($table);
+               if(isset(self::$WeIsI[$ut]))
+               {
+                       return $table;
+               }
+               if(isset(self::$IrregularP[$ut]))
+               {
+                       return self::$IrregularP[$ut];
+               }
+               $len = strlen($table);
+               $lastc = $ut[$len-1];
+               $lastc2 = substr($ut,$len-2);
+               switch ($lastc) {
+               case 'S':
+                       return $table.'es';     
+               case 'Y':
+                       return substr($table,0,$len-1).'ies';
+               case 'X':       
+                       return $table.'es';
+               case 'H': 
+                       if ($lastc2 == 'CH' || $lastc2 == 'SH')
+                               return $table.'es';
+               default:
+                       return $table.'s';
+               }
+       }
+       
+       // CFR Lamest singular inflector ever - @todo Make it real!
+       // Note: There is an assumption here...and it is that the argument's length >= 4
+       function _singularize($table)
+       {
+       
+               if (!ADODB_Active_Record::$_changeNames) return $table;
+       
+               $ut = strtoupper($table);
+               if(isset(self::$WeIsI[$ut]))
+               {
+                       return $table;
+               }
+               if(isset(self::$IrregularS[$ut]))
+               {
+                       return self::$IrregularS[$ut];
+               }
+               $len = strlen($table);
+               if($ut[$len-1] != 'S')
+                       return $table; // I know...forget oxen
+               if($ut[$len-2] != 'E')
+                       return substr($table, 0, $len-1);
+               switch($ut[$len-3])
+               {
+                       case 'S':
+                       case 'X':
+                               return substr($table, 0, $len-2);
+                       case 'I':
+                               return substr($table, 0, $len-3) . 'y';
+                       case 'H';
+                               if($ut[$len-4] == 'C' || $ut[$len-4] == 'S')
+                                       return substr($table, 0, $len-2);
+                       default:
+                               return substr($table, 0, $len-1); // ?
+               }
+       }
+
+       /*
+        * ar->foreignName will contain the name of the tables associated with this table because
+        * these other tables' rows may also be referenced by this table using theirname_id or the provided
+        * foreign keys (this index name is stored in ar->foreignKey)
+        *
+        * this-table.id = other-table-#1.this-table_id
+        *               = other-table-#2.this-table_id
+        */
+       function hasMany($foreignRef,$foreignKey=false)
+       {
+               $ar = new ADODB_Active_Record($foreignRef);
+               $ar->foreignName = $foreignRef;
+               $ar->UpdateActiveTable();
+               $ar->foreignKey = ($foreignKey) ? $foreignKey : strtolower(get_class($this)) . self::$_foreignSuffix;
+
+               $table =& $this->TableInfo();
+               if(!isset($table->_hasMany[$foreignRef]))
+               {
+                       $table->_hasMany[$foreignRef] = $ar;
+                       $table->updateColsCount();
+               }
+# @todo Can I make this guy be lazy?
+               $this->$foreignRef = $table->_hasMany[$foreignRef]; // WATCHME Removed assignment by ref. to please __get()
+       }
+
+       /**
+        * ar->foreignName will contain the name of the tables associated with this table because
+        * this table's rows may also be referenced by those tables using thistable_id or the provided
+        * foreign keys (this index name is stored in ar->foreignKey)
+        *
+        * this-table.other-table_id = other-table.id
+        */
+       function belongsTo($foreignRef,$foreignKey=false)
+       {
+               global $inflector;
+
+               $ar = new ADODB_Active_Record($this->_pluralize($foreignRef));
+               $ar->foreignName = $foreignRef;
+               $ar->UpdateActiveTable();
+               $ar->foreignKey = ($foreignKey) ? $foreignKey : $ar->foreignName . self::$_foreignSuffix;
+               
+               $table =& $this->TableInfo();
+               if(!isset($table->_belongsTo[$foreignRef]))
+               {
+                       $table->_belongsTo[$foreignRef] = $ar;
+                       $table->updateColsCount();
+               }
+               $this->$foreignRef = $table->_belongsTo[$foreignRef];
+       }
+
+       /**
+        * __get Access properties - used for lazy loading
+        * 
+        * @param mixed $name 
+        * @access protected
+        * @return void
+        */
+       function __get($name)
+       {
+               return $this->LoadRelations($name, '', -1. -1);
+       }
+
+       function LoadRelations($name, $whereOrderBy, $offset=-1, $limit=-1)
+       {
+               $extras = array();
+               if($offset >= 0) $extras['offset'] = $offset;
+               if($limit >= 0) $extras['limit'] = $limit;
+               $table =& $this->TableInfo();
+               
+               if (strlen($whereOrderBy)) 
+                       if (!preg_match('/^[ \n\r]*AND/i',$whereOrderBy))
+                               if (!preg_match('/^[ \n\r]*ORDER[ \n\r]/i',$whereOrderBy))
+                                       $whereOrderBy = 'AND '.$whereOrderBy;
+                                       
+               if(!empty($table->_belongsTo[$name]))
+               {
+                       $obj = $table->_belongsTo[$name];
+                       $columnName = $obj->foreignKey;
+                       if(empty($this->$columnName))
+                               $this->$name = null;
+                       else
+                       {
+                               if(($k = reset($obj->TableInfo()->keys)))
+                                       $belongsToId = $k;
+                               else
+                                       $belongsToId = 'id';
+                               
+                               $arrayOfOne =
+                                       $obj->Find(
+                                               $belongsToId.'='.$this->$columnName.' '.$whereOrderBy, false, false, $extras);
+                               $this->$name = $arrayOfOne[0];
+                       }
+                       return $this->$name;
+               }
+               if(!empty($table->_hasMany[$name]))
+               {
+                       $obj = $table->_hasMany[$name];
+                       if(($k = reset($table->keys)))
+                               $hasManyId   = $k;
+                       else
+                               $hasManyId   = 'id';                    
+
+                       $this->$name =
+                               $obj->Find(
+                                       $obj->foreignKey.'='.$this->$hasManyId.' '.$whereOrderBy, false, false, $extras);
+                       return $this->$name;
+               }
+       }
+       //////////////////////////////////
+       
+       // update metadata
+       function UpdateActiveTable($pkeys=false,$forceUpdate=false)
+       {
+       global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS , $ADODB_CACHE_DIR, $ADODB_ACTIVE_CACHESECS;
+       global $ADODB_ACTIVE_DEFVALS, $ADODB_FETCH_MODE;
+
+               $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
+
+               $table = $this->_table;
+               $tables = $activedb->tables;
+               $tableat = $this->_tableat;
+               if (!$forceUpdate && !empty($tables[$tableat])) {
+
+                       $tobj = $tables[$tableat];
+                       foreach($tobj->flds as $name => $fld) {
+                       if ($ADODB_ACTIVE_DEFVALS && isset($fld->default_value)) 
+                               $this->$name = $fld->default_value;
+                       else
+                               $this->$name = null;
+                       }
+                       return;
+               }
+               
+               $db = $activedb->db;
+               $fname = $ADODB_CACHE_DIR . '/adodb_' . $db->databaseType . '_active_'. $table . '.cache';
+               if (!$forceUpdate && $ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR && file_exists($fname)) {
+                       $fp = fopen($fname,'r');
+                       @flock($fp, LOCK_SH);
+                       $acttab = unserialize(fread($fp,100000));
+                       fclose($fp);
+                       if ($acttab->_created + $ADODB_ACTIVE_CACHESECS - (abs(rand()) % 16) > time()) { 
+                               // abs(rand()) randomizes deletion, reducing contention to delete/refresh file
+                               // ideally, you should cache at least 32 secs
+                               $activedb->tables[$table] = $acttab;
+                               
+                               //if ($db->debug) ADOConnection::outp("Reading cached active record file: $fname");
+                               return;
+                       } else if ($db->debug) {
+                               ADOConnection::outp("Refreshing cached active record file: $fname");
+                       }
+               }
+               $activetab = new ADODB_Active_Table();
+               $activetab->name = $table;
+               
+               $save = $ADODB_FETCH_MODE;
+               $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
+               if ($db->fetchMode !== false) $savem = $db->SetFetchMode(false);
+               
+               $cols = $db->MetaColumns($table);
+               
+               if (isset($savem)) $db->SetFetchMode($savem);
+               $ADODB_FETCH_MODE = $save;
+               
+               if (!$cols) {
+                       $this->Error("Invalid table name: $table",'UpdateActiveTable'); 
+                       return false;
+               }
+               $fld = reset($cols);
+               if (!$pkeys) {
+                       if (isset($fld->primary_key)) {
+                               $pkeys = array();
+                               foreach($cols as $name => $fld) {
+                                       if (!empty($fld->primary_key)) $pkeys[] = $name;
+                               }
+                       } else  
+                               $pkeys = $this->GetPrimaryKeys($db, $table);
+               }
+               if (empty($pkeys)) {
+                       $this->Error("No primary key found for table $table",'UpdateActiveTable');
+                       return false;
+               }
+               
+               $attr = array();
+               $keys = array();
+               
+               switch($ADODB_ASSOC_CASE) {
+               case 0:
+                       foreach($cols as $name => $fldobj) {
+                               $name = strtolower($name);
+                if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value))
+                    $this->$name = $fldobj->default_value;
+                else
+                                       $this->$name = null;
+                               $attr[$name] = $fldobj;
+                       }
+                       foreach($pkeys as $k => $name) {
+                               $keys[strtolower($name)] = strtolower($name);
+                       }
+                       break;
+                       
+               case 1: 
+                       foreach($cols as $name => $fldobj) {
+                               $name = strtoupper($name);
+               
+                if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value))
+                    $this->$name = $fldobj->default_value;
+                else
+                                       $this->$name = null;
+                               $attr[$name] = $fldobj;
+                       }
+                       
+                       foreach($pkeys as $k => $name) {
+                               $keys[strtoupper($name)] = strtoupper($name);
+                       }
+                       break;
+               default:
+                       foreach($cols as $name => $fldobj) {
+                               $name = ($fldobj->name);
+                
+                if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value))
+                    $this->$name = $fldobj->default_value;
+                else
+                                       $this->$name = null;
+                               $attr[$name] = $fldobj;
+                       }
+                       foreach($pkeys as $k => $name) {
+                               $keys[$name] = $cols[$name]->name;
+                       }
+                       break;
+               }
+               
+               $activetab->keys = $keys;
+               $activetab->flds = $attr;
+               $activetab->updateColsCount();
+
+               if ($ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR) {
+                       $activetab->_created = time();
+                       $s = serialize($activetab);
+                       if (!function_exists('adodb_write_file')) include(ADODB_DIR.'/adodb-csvlib.inc.php');
+                       adodb_write_file($fname,$s);
+               }
+               if (isset($activedb->tables[$table])) {
+                       $oldtab = $activedb->tables[$table];
+               
+                       if ($oldtab) $activetab->_belongsTo = $oldtab->_belongsTo;
+                       if ($oldtab) $activetab->_hasMany = $oldtab->_hasMany;
+               }
+               $activedb->tables[$table] = $activetab;
+       }
+       
+       function GetPrimaryKeys(&$db, $table)
+       {
+               return $db->MetaPrimaryKeys($table);
+       }
+       
+       // error handler for both PHP4+5. 
+       function Error($err,$fn)
+       {
+       global $_ADODB_ACTIVE_DBS;
+       
+               $fn = get_class($this).'::'.$fn;
+               $this->_lasterr = $fn.': '.$err;
+               
+               if ($this->_dbat < 0) $db = false;
+               else {
+                       $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
+                       $db = $activedb->db;
+               }
+               
+               if (function_exists('adodb_throw')) {   
+                       if (!$db) adodb_throw('ADOdb_Active_Record', $fn, -1, $err, 0, 0, false);
+                       else adodb_throw($db->databaseType, $fn, -1, $err, 0, 0, $db);
+               } else
+                       if (!$db || $db->debug) ADOConnection::outp($this->_lasterr);
+               
+       }
+       
+       // return last error message
+       function ErrorMsg()
+       {
+               if (!function_exists('adodb_throw')) {
+                       if ($this->_dbat < 0) $db = false;
+                       else $db = $this->DB();
+               
+                       // last error could be database error too
+                       if ($db && $db->ErrorMsg()) return $db->ErrorMsg();
+               }
+               return $this->_lasterr;
+       }
+       
+       function ErrorNo() 
+       {
+               if ($this->_dbat < 0) return -9999; // no database connection...
+               $db = $this->DB();
+               
+               return (int) $db->ErrorNo();
+       }
+
+
+       // retrieve ADOConnection from _ADODB_Active_DBs
+       function DB()
+       {
+       global $_ADODB_ACTIVE_DBS;
+       
+               if ($this->_dbat < 0) {
+                       $false = false;
+                       $this->Error("No database connection set: use ADOdb_Active_Record::SetDatabaseAdaptor(\$db)", "DB");
+                       return $false;
+               }
+               $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
+               $db = $activedb->db;
+               return $db;
+       }
+       
+       // retrieve ADODB_Active_Table
+       function &TableInfo()
+       {
+       global $_ADODB_ACTIVE_DBS;
+       
+               $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
+               $table = $activedb->tables[$this->_tableat];
+               return $table;
+       }
+       
+       
+       // I have an ON INSERT trigger on a table that sets other columns in the table.
+       // So, I find that for myTable, I want to reload an active record after saving it. -- Malcolm Cook
+       function Reload()
+       {
+               $db =& $this->DB(); if (!$db) return false;
+               $table =& $this->TableInfo();
+               $where = $this->GenWhere($db, $table);
+               return($this->Load($where));
+       }
+
+       
+       // set a numeric array (using natural table field ordering) as object properties
+       function Set(&$row)
+       {
+       global $ACTIVE_RECORD_SAFETY;
+       
+               $db = $this->DB();
+               
+               if (!$row) {
+                       $this->_saved = false;          
+                       return false;
+               }
+               
+               $this->_saved = true;
+               
+               $table = $this->TableInfo();
+               $sizeofFlds = sizeof($table->flds);
+               $sizeofRow  = sizeof($row);
+               if ($ACTIVE_RECORD_SAFETY && $table->_colsCount != $sizeofRow && $sizeofFlds != $sizeofRow) {
+            # <AP>
+            $bad_size = TRUE;
+       if($sizeofRow == 2 * $table->_colsCount || $sizeofRow == 2 * $sizeofFlds) {
+                // Only keep string keys
+                $keys = array_filter(array_keys($row), 'is_string');
+                if (sizeof($keys) == sizeof($table->flds))
+                    $bad_size = FALSE;
+            }
+            if ($bad_size) {
+                       $this->Error("Table structure of $this->_table has changed","Load");
+                       return false;
+               }
+            # </AP>
+               }
+        else
+               $keys = array_keys($row);
+        # <AP>
+        reset($keys);
+        $this->_original = array();
+               foreach($table->flds as $name=>$fld)
+               {
+            $value = $row[current($keys)];
+                       $this->$name = $value;
+            $this->_original[] = $value;
+            if(!next($keys)) break;
+               }
+               $table =& $this->TableInfo();
+               foreach($table->_belongsTo as $foreignTable)
+               {
+                       $ft = $foreignTable->TableInfo();
+                       $propertyName = $ft->name;
+                       foreach($ft->flds as $name=>$fld)
+                       {
+                               $value = $row[current($keys)];
+                               $foreignTable->$name = $value;
+                               $foreignTable->_original[] = $value;
+                               if(!next($keys)) break;
+                       }
+               }
+               foreach($table->_hasMany as $foreignTable)
+               {
+                       $ft = $foreignTable->TableInfo();
+                       foreach($ft->flds as $name=>$fld)
+                       {
+                               $value = $row[current($keys)];
+                               $foreignTable->$name = $value;
+                               $foreignTable->_original[] = $value;
+                               if(!next($keys)) break;
+                       }
+               }
+        # </AP>
+               return true;
+       }
+       
+       // get last inserted id for INSERT
+       function LastInsertID(&$db,$fieldname)
+       {
+               if ($db->hasInsertID)
+                       $val = $db->Insert_ID($this->_table,$fieldname);
+               else
+                       $val = false;
+                       
+               if (is_null($val) || $val === false) {
+                       // this might not work reliably in multi-user environment
+                       return $db->GetOne("select max(".$fieldname.") from ".$this->_table);
+               }
+               return $val;
+       }
+       
+       // quote data in where clause
+       function doquote(&$db, $val,$t)
+       {
+               switch($t) {
+               case 'D':
+               case 'T':
+                       if (empty($val)) return 'null';
+                       
+               case 'C':
+               case 'X':
+                       if (is_null($val)) return 'null';
+                       
+                       if (strlen($val)>1 && 
+                               (strncmp($val,"'",1) != 0 || substr($val,strlen($val)-1,1) != "'")) { 
+                               return $db->qstr($val);
+                               break;
+                       }
+               default:
+                       return $val;
+                       break;
+               }
+       }
+       
+       // generate where clause for an UPDATE/SELECT
+       function GenWhere(&$db, &$table)
+       {
+               $keys = $table->keys;
+               $parr = array();
+               
+               foreach($keys as $k) {
+                       $f = $table->flds[$k];
+                       if ($f) {
+                               $parr[] = $k.' = '.$this->doquote($db,$this->$k,$db->MetaType($f->type));
+                       }
+               }
+               return implode(' and ', $parr);
+       }
+       
+       
+       //------------------------------------------------------------ Public functions below
+       
+       function Load($where=null,$bindarr=false)
+       {
+               $db = $this->DB(); if (!$db) return false;
+               $this->_where = $where;
+               
+               $save = $db->SetFetchMode(ADODB_FETCH_NUM);
+               $qry = "select * from ".$this->_table;
+               $table =& $this->TableInfo();
+
+               if(($k = reset($table->keys)))
+                       $hasManyId   = $k;
+               else
+                       $hasManyId   = 'id';
+               
+               foreach($table->_belongsTo as $foreignTable)
+               {
+                       if(($k = reset($foreignTable->TableInfo()->keys)))
+                       {
+                               $belongsToId = $k;
+                       }
+                       else
+                       {
+                               $belongsToId = 'id';
+                       }
+                       $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '.
+                               $this->_table.'.'.$foreignTable->foreignKey.'='.
+                               $foreignTable->_table.'.'.$belongsToId;
+               }
+               foreach($table->_hasMany as $foreignTable)
+               {
+                       $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '.
+                               $this->_table.'.'.$hasManyId.'='.
+                               $foreignTable->_table.'.'.$foreignTable->foreignKey;
+               }
+               if($where)
+                       $qry .= ' WHERE '.$where;
+               
+               // Simple case: no relations. Load row and return.
+               if((count($table->_hasMany) + count($table->_belongsTo)) < 1)
+               {
+                       $row = $db->GetRow($qry,$bindarr);
+                       if(!$row)
+                               return false;
+                       $db->SetFetchMode($save);
+                       return $this->Set($row);
+               }
+               
+               // More complex case when relations have to be collated
+               $rows = $db->GetAll($qry,$bindarr);
+               if(!$rows)
+                       return false;
+               $db->SetFetchMode($save);
+               if(count($rows) < 1)
+                       return false;
+               $class = get_class($this);
+               $isFirstRow = true;
+               
+               if(($k = reset($this->TableInfo()->keys)))
+                       $myId   = $k;
+               else
+                       $myId   = 'id';
+               $index = 0; $found = false;
+               /** @todo Improve by storing once and for all in table metadata */
+               /** @todo Also re-use info for hasManyId */
+               foreach($this->TableInfo()->flds as $fld)
+               {
+                       if($fld->name == $myId)
+                       {
+                               $found = true;
+                               break;
+                       }
+                       $index++;
+               }
+               if(!$found)
+                       $this->outp_throw("Unable to locate key $myId for $class in Load()",'Load');
+               
+               foreach($rows as $row)
+               {
+                       $rowId = intval($row[$index]);
+                       if($rowId > 0)
+                       {
+                               if($isFirstRow)
+                               {
+                                       $isFirstRow = false;
+                                       if(!$this->Set($row))
+                                               return false;
+                               }
+                               $obj = new $class($table,false,$db);
+                               $obj->Set($row);
+                               // TODO Copy/paste code below: bad!
+                               if(count($table->_hasMany) > 0)
+                               {
+                                       foreach($table->_hasMany as $foreignTable)
+                                       {
+                                               $foreignName = $foreignTable->foreignName;
+                                               if(!empty($obj->$foreignName))
+                                               {
+                                                       if(!is_array($this->$foreignName))
+                                                       {
+                                                               $foreignObj = $this->$foreignName;
+                                                               $this->$foreignName = array(clone($foreignObj));
+                                                       }
+                                                       else
+                                                       {
+                                                               $foreignObj = $obj->$foreignName;
+                                                               array_push($this->$foreignName, clone($foreignObj));
+                                                       }
+                                               }
+                                       }
+                               }
+                               if(count($table->_belongsTo) > 0)
+                               {
+                                       foreach($table->_belongsTo as $foreignTable)
+                                       {
+                                               $foreignName = $foreignTable->foreignName;
+                                               if(!empty($obj->$foreignName))
+                                               {
+                                                       if(!is_array($this->$foreignName))
+                                                       {
+                                                               $foreignObj = $this->$foreignName;
+                                                               $this->$foreignName = array(clone($foreignObj));
+                                                       }
+                                                       else
+                                                       {
+                                                               $foreignObj = $obj->$foreignName;
+                                                               array_push($this->$foreignName, clone($foreignObj));
+                                                       }
+                                               }
+                                       }
+                               }                               
+                       }
+               }
+               return true;
+       }
+       
+       // false on error
+       function Save()
+       {
+               if ($this->_saved) $ok = $this->Update();
+               else $ok = $this->Insert();
+               
+               return $ok;
+       }
+       
+       // CFR: Sometimes we may wish to consider that an object is not to be replaced but inserted.
+       // Sample use case: an 'undo' command object (after a delete())
+       function Dirty()
+       {
+               $this->_saved = false;
+       }
+
+       // false on error
+       function Insert()
+       {
+               $db = $this->DB(); if (!$db) return false;
+               $cnt = 0;
+               $table = $this->TableInfo();
+               
+               $valarr = array();
+               $names = array();
+               $valstr = array();
+
+               foreach($table->flds as $name=>$fld) {
+                       $val = $this->$name;
+                       if(!is_null($val) || !array_key_exists($name, $table->keys)) {
+                               $valarr[] = $val;
+                               $names[] = $name;
+                               $valstr[] = $db->Param($cnt);
+                               $cnt += 1;
+                       }
+               }
+               
+               if (empty($names)){
+                       foreach($table->flds as $name=>$fld) {
+                               $valarr[] = null;
+                               $names[] = $name;
+                               $valstr[] = $db->Param($cnt);
+                               $cnt += 1;
+                       }
+               }
+               $sql = 'INSERT INTO '.$this->_table."(".implode(',',$names).') VALUES ('.implode(',',$valstr).')';
+               $ok = $db->Execute($sql,$valarr);
+               
+               if ($ok) {
+                       $this->_saved = true;
+                       $autoinc = false;
+                       foreach($table->keys as $k) {
+                               if (is_null($this->$k)) {
+                                       $autoinc = true;
+                                       break;
+                               }
+                       }
+                       if ($autoinc && sizeof($table->keys) == 1) {
+                               $k = reset($table->keys);
+                               $this->$k = $this->LastInsertID($db,$k);
+                       }
+               }
+               
+               $this->_original = $valarr;
+               return !empty($ok);
+       }
+       
+       function Delete()
+       {
+               $db = $this->DB(); if (!$db) return false;
+               $table = $this->TableInfo();
+               
+               $where = $this->GenWhere($db,$table);
+               $sql = 'DELETE FROM '.$this->_table.' WHERE '.$where;
+               $ok = $db->Execute($sql);
+               
+               return $ok ? true : false;
+       }
+       
+       // returns an array of active record objects
+       function Find($whereOrderBy,$bindarr=false,$pkeysArr=false,$extra=array())
+       {
+               $db = $this->DB(); if (!$db || empty($this->_table)) return false;
+               $table =& $this->TableInfo();
+               $arr = $db->GetActiveRecordsClass(get_class($this),$this, $whereOrderBy,$bindarr,$pkeysArr,$extra,
+                       array('foreignName'=>$this->foreignName, 'belongsTo'=>$table->_belongsTo, 'hasMany'=>$table->_hasMany));
+               return $arr;
+       }
+       
+       // CFR: In introduced this method to ensure that inner workings are not disturbed by
+       // subclasses...for instance when GetActiveRecordsClass invokes Find()
+       // Why am I not invoking parent::Find?
+       // Shockingly because I want to preserve PHP4 compatibility.
+       function packageFind($whereOrderBy,$bindarr=false,$pkeysArr=false,$extra=array())
+       {
+               $db = $this->DB(); if (!$db || empty($this->_table)) return false;
+               $table =& $this->TableInfo();
+               $arr = $db->GetActiveRecordsClass(get_class($this),$this, $whereOrderBy,$bindarr,$pkeysArr,$extra,
+                       array('foreignName'=>$this->foreignName, 'belongsTo'=>$table->_belongsTo, 'hasMany'=>$table->_hasMany));
+               return $arr;
+       }
+
+       // returns 0 on error, 1 on update, 2 on insert
+       function Replace()
+       {
+       global $ADODB_ASSOC_CASE;
+               
+               $db = $this->DB(); if (!$db) return false;
+               $table = $this->TableInfo();
+               
+               $pkey = $table->keys;
+               
+               foreach($table->flds as $name=>$fld) {
+                       $val = $this->$name;
+                       /*
+                       if (is_null($val)) {
+                               if (isset($fld->not_null) && $fld->not_null) {
+                                       if (isset($fld->default_value) && strlen($fld->default_value)) continue;
+                                       else {
+                                               $this->Error("Cannot update null into $name","Replace");
+                                               return false;
+                                       }
+                               }
+                       }*/
+                       if (is_null($val) && !empty($fld->auto_increment)) {
+               continue;
+            }
+                       $t = $db->MetaType($fld->type);
+                       $arr[$name] = $this->doquote($db,$val,$t);
+                       $valarr[] = $val;
+               }
+               
+               if (!is_array($pkey)) $pkey = array($pkey);
+               
+               
+               if ($ADODB_ASSOC_CASE == 0) 
+                       foreach($pkey as $k => $v)
+                               $pkey[$k] = strtolower($v);
+               elseif ($ADODB_ASSOC_CASE == 1) 
+                       foreach($pkey as $k => $v)
+                               $pkey[$k] = strtoupper($v);
+                               
+               $ok = $db->Replace($this->_table,$arr,$pkey);
+               if ($ok) {
+                       $this->_saved = true; // 1= update 2=insert
+                       if ($ok == 2) {
+                               $autoinc = false;
+                               foreach($table->keys as $k) {
+                                       if (is_null($this->$k)) {
+                                               $autoinc = true;
+                                               break;
+                                       }
+                               }
+                               if ($autoinc && sizeof($table->keys) == 1) {
+                                       $k = reset($table->keys);
+                                       $this->$k = $this->LastInsertID($db,$k);
+                               }
+                       }
+                       
+                       $this->_original = $valarr;
+               } 
+               return $ok;
+       }
+
+       // returns 0 on error, 1 on update, -1 if no change in data (no update)
+       function Update()
+       {
+               $db = $this->DB(); if (!$db) return false;
+               $table = $this->TableInfo();
+               
+               $where = $this->GenWhere($db, $table);
+               
+               if (!$where) {
+                       $this->error("Where missing for table $table", "Update");
+                       return false;
+               }
+               $valarr = array(); 
+               $neworig = array();
+               $pairs = array();
+               $i = -1;
+               $cnt = 0;
+               foreach($table->flds as $name=>$fld) {
+                       $i += 1;
+                       $val = $this->$name;
+                       $neworig[] = $val;
+                       
+                       if (isset($table->keys[$name])) {
+                               continue;
+                       }
+                       
+                       if (is_null($val)) {
+                               if (isset($fld->not_null) && $fld->not_null) {
+                                       if (isset($fld->default_value) && strlen($fld->default_value)) continue;
+                                       else {
+                                               $this->Error("Cannot set field $name to NULL","Update");
+                                               return false;
+                                       }
+                               }
+                       }
+                       
+                       if (isset($this->_original[$i]) && $val == $this->_original[$i]) {
+                               continue;
+                       }                       
+                       $valarr[] = $val;
+                       $pairs[] = $name.'='.$db->Param($cnt);
+                       $cnt += 1;
+               }
+               
+               
+               if (!$cnt) return -1;
+               $sql = 'UPDATE '.$this->_table." SET ".implode(",",$pairs)." WHERE ".$where;
+               $ok = $db->Execute($sql,$valarr);
+               if ($ok) {
+                       $this->_original = $neworig;
+                       return 1;
+               }
+               return 0;
+       }
+       
+       function GetAttributeNames()
+       {
+               $table = $this->TableInfo();
+               if (!$table) return false;
+               return array_keys($table->flds);
+       }
+       
+};
+
+function adodb_GetActiveRecordsClass(&$db, $class, $tableObj,$whereOrderBy,$bindarr, $primkeyArr,
+                       $extra, $relations)
+{
+       global $_ADODB_ACTIVE_DBS;
+       
+               if (empty($extra['loading'])) $extra['loading'] = ADODB_LAZY_AR;
+               
+               $save = $db->SetFetchMode(ADODB_FETCH_NUM);
+               $table = &$tableObj->_table;
+               $tableInfo =& $tableObj->TableInfo();
+               if(($k = reset($tableInfo->keys)))
+                       $myId   = $k;
+               else
+                       $myId   = 'id';
+               $index = 0; $found = false;
+               /** @todo Improve by storing once and for all in table metadata */
+               /** @todo Also re-use info for hasManyId */
+               foreach($tableInfo->flds as $fld)
+               {
+                       if($fld->name == $myId)
+                       {
+                               $found = true;
+                               break;
+                       }
+                       $index++;
+               }
+               if(!$found)
+                       $db->outp_throw("Unable to locate key $myId for $class in GetActiveRecordsClass()",'GetActiveRecordsClass');
+               
+               $qry = "select * from ".$table;
+               if(ADODB_JOIN_AR == $extra['loading'])
+               {
+                       if(!empty($relations['belongsTo']))
+                       {
+                               foreach($relations['belongsTo'] as $foreignTable)
+                               {
+                                       if(($k = reset($foreignTable->TableInfo()->keys)))
+                                       {
+                                               $belongsToId = $k;
+                                       }
+                                       else
+                                       {
+                                               $belongsToId = 'id';
+                                       }
+
+                                       $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '.
+                                               $table.'.'.$foreignTable->foreignKey.'='.
+                                               $foreignTable->_table.'.'.$belongsToId;
+                               }
+                       }
+                       if(!empty($relations['hasMany']))
+                       {
+                               if(empty($relations['foreignName']))
+                                       $db->outp_throw("Missing foreignName is relation specification in GetActiveRecordsClass()",'GetActiveRecordsClass');
+                               if(($k = reset($tableInfo->keys)))
+                                       $hasManyId   = $k;
+                               else
+                                       $hasManyId   = 'id';
+
+                               foreach($relations['hasMany'] as $foreignTable)
+                               {
+                                       $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '.
+                                               $table.'.'.$hasManyId.'='.
+                                               $foreignTable->_table.'.'.$foreignTable->foreignKey;
+                               }
+                       }
+               }
+               if (!empty($whereOrderBy))
+                       $qry .= ' WHERE '.$whereOrderBy;
+               if(isset($extra['limit']))
+               {
+                       $rows = false;
+                       if(isset($extra['offset'])) {
+                               $rs = $db->SelectLimit($qry, $extra['limit'], $extra['offset']);
+                       } else {
+                               $rs = $db->SelectLimit($qry, $extra['limit']);
+                       }
+                       if ($rs) {
+                               while (!$rs->EOF) {
+                                       $rows[] = $rs->fields;
+                                       $rs->MoveNext();
+                               }
+                       }
+               } else
+                       $rows = $db->GetAll($qry,$bindarr);
+                       
+               $db->SetFetchMode($save);
+               
+               $false = false;
+               
+               if ($rows === false) {  
+                       return $false;
+               }
+               
+               
+               if (!isset($_ADODB_ACTIVE_DBS)) {
+                       include(ADODB_DIR.'/adodb-active-record.inc.php');
+               }       
+               if (!class_exists($class)) {
+                       $db->outp_throw("Unknown class $class in GetActiveRecordsClass()",'GetActiveRecordsClass');
+                       return $false;
+               }
+               $uniqArr = array(); // CFR Keep track of records for relations
+               $arr = array();
+               // arrRef will be the structure that knows about our objects.
+               // It is an associative array.
+               // We will, however, return arr, preserving regular 0.. order so that
+               // obj[0] can be used by app developpers.
+               $arrRef = array();
+               $bTos = array(); // Will store belongTo's indices if any
+               foreach($rows as $row) {
+               
+                       $obj = new $class($table,$primkeyArr,$db);
+                       if ($obj->ErrorNo()){
+                               $db->_errorMsg = $obj->ErrorMsg();
+                               return $false;
+                       }
+                       $obj->Set($row);
+                       // CFR: FIXME: Insane assumption here:
+                       // If the first column returned is an integer, then it's a 'id' field
+                       // And to make things a bit worse, I use intval() rather than is_int() because, in fact,
+                       // $row[0] is not an integer.
+                       //
+                       // So, what does this whole block do?
+                       // When relationships are found, we perform JOINs. This is fast. But not accurate:
+                       // instead of returning n objects with their n' associated cousins,
+                       // we get n*n' objects. This code fixes this.
+                       // Note: to-many relationships mess around with the 'limit' parameter
+                       $rowId = intval($row[$index]);
+
+                       if(ADODB_WORK_AR == $extra['loading'])
+                       {
+                               $arrRef[$rowId] = $obj;
+                               $arr[] = &$arrRef[$rowId];
+                               if(!isset($indices))
+                                       $indices = $rowId;
+                               else
+                                       $indices .= ','.$rowId;
+                               if(!empty($relations['belongsTo']))
+                               {
+                                       foreach($relations['belongsTo'] as $foreignTable)
+                                       {
+                                               $foreignTableRef = $foreignTable->foreignKey;
+                                               // First array: list of foreign ids we are looking for
+                                               if(empty($bTos[$foreignTableRef]))
+                                                       $bTos[$foreignTableRef] = array();
+                                               // Second array: list of ids found
+                                               if(empty($obj->$foreignTableRef))
+                                                       continue;
+                                               if(empty($bTos[$foreignTableRef][$obj->$foreignTableRef]))
+                                                       $bTos[$foreignTableRef][$obj->$foreignTableRef] = array();
+                                               $bTos[$foreignTableRef][$obj->$foreignTableRef][] = $obj;
+                                       }
+                               }
+                               continue;
+                       }
+
+                       if($rowId>0)
+                       {
+                               if(ADODB_JOIN_AR == $extra['loading'])
+                               {
+                                       $isNewObj = !isset($uniqArr['_'.$row[0]]); 
+                                       if($isNewObj)
+                                               $uniqArr['_'.$row[0]] = $obj;
+
+                                       // TODO Copy/paste code below: bad!
+                                       if(!empty($relations['hasMany']))
+                                       {
+                                               foreach($relations['hasMany'] as $foreignTable)
+                                               {
+                                                       $foreignName = $foreignTable->foreignName;
+                                                       if(!empty($obj->$foreignName))
+                                                       {
+                                                               $masterObj = &$uniqArr['_'.$row[0]];
+                                                               // Assumption: this property exists in every object since they are instances of the same class
+                                                               if(!is_array($masterObj->$foreignName))
+                                                               {
+                                                                       // Pluck!
+                                                                       $foreignObj = $masterObj->$foreignName;
+                                                                       $masterObj->$foreignName = array(clone($foreignObj));
+                                                               }
+                                                               else
+                                                               {
+                                                                       // Pluck pluck!
+                                                                       $foreignObj = $obj->$foreignName;
+                                                                       array_push($masterObj->$foreignName, clone($foreignObj));
+                                                               }
+                                                       }
+                                               }
+                                       }
+                                       if(!empty($relations['belongsTo']))
+                                       {
+                                               foreach($relations['belongsTo'] as $foreignTable)
+                                               {
+                                                       $foreignName = $foreignTable->foreignName;
+                                                       if(!empty($obj->$foreignName))
+                                                       {
+                                                               $masterObj = &$uniqArr['_'.$row[0]];
+                                                               // Assumption: this property exists in every object since they are instances of the same class
+                                                               if(!is_array($masterObj->$foreignName))
+                                                               {
+                                                                       // Pluck!
+                                                                       $foreignObj = $masterObj->$foreignName;
+                                                                       $masterObj->$foreignName = array(clone($foreignObj));
+                                                               }
+                                                               else
+                                                               {
+                                                                       // Pluck pluck!
+                                                                       $foreignObj = $obj->$foreignName;
+                                                                       array_push($masterObj->$foreignName, clone($foreignObj));
+                                                               }
+                                                       }
+                                               }
+                                       }
+                                       if(!$isNewObj)
+                                               unset($obj); // We do not need this object itself anymore and do not want it re-added to the main array                                 
+                               }
+                               else if(ADODB_LAZY_AR == $extra['loading'])
+                               {
+                                       // Lazy loading: we need to give AdoDb a hint that we have not really loaded
+                                       // anything, all the while keeping enough information on what we wish to load.
+                                       // Let's do this by keeping the relevant info in our relationship arrays
+                                       // but get rid of the actual properties.
+                                       // We will then use PHP's __get to load these properties on-demand.
+                                       if(!empty($relations['hasMany']))
+                                       {
+                                               foreach($relations['hasMany'] as $foreignTable)
+                                               {
+                                                       $foreignName = $foreignTable->foreignName;
+                                                       if(!empty($obj->$foreignName))
+                                                       {
+                                                               unset($obj->$foreignName);
+                                                       }
+                                               }
+                                       }
+                                       if(!empty($relations['belongsTo']))
+                                       {
+                                               foreach($relations['belongsTo'] as $foreignTable)
+                                               {
+                                                       $foreignName = $foreignTable->foreignName;
+                                                       if(!empty($obj->$foreignName))
+                                                       {
+                                                               unset($obj->$foreignName);
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+
+                       if(isset($obj))
+                               $arr[] = $obj;
+               }
+
+               if(ADODB_WORK_AR == $extra['loading'])
+               {
+                       // The best of both worlds?
+                       // Here, the number of queries is constant: 1 + n*relationship.
+                       // The second query will allow us to perform a good join
+                       // while preserving LIMIT etc.
+                       if(!empty($relations['hasMany']))
+                       {
+                               foreach($relations['hasMany'] as $foreignTable)
+                               {
+                                       $foreignName = $foreignTable->foreignName;
+                                       $className = ucfirst($foreignTable->_singularize($foreignName));
+                                       $obj = new $className();
+                                       $dbClassRef = $foreignTable->foreignKey;
+                                       $objs = $obj->packageFind($dbClassRef.' IN ('.$indices.')');
+                                       foreach($objs as $obj)
+                                       {
+                                               if(!is_array($arrRef[$obj->$dbClassRef]->$foreignName))
+                                                       $arrRef[$obj->$dbClassRef]->$foreignName = array();
+                                               array_push($arrRef[$obj->$dbClassRef]->$foreignName, $obj);
+                                       }
+                               }
+                               
+                       }
+                       if(!empty($relations['belongsTo']))
+                       {
+                               foreach($relations['belongsTo'] as $foreignTable)
+                               {
+                                       $foreignTableRef = $foreignTable->foreignKey;
+                                       if(empty($bTos[$foreignTableRef]))
+                                               continue;
+                                       if(($k = reset($foreignTable->TableInfo()->keys)))
+                                       {
+                                               $belongsToId = $k;
+                                       }
+                                       else
+                                       {
+                                               $belongsToId = 'id';
+                                       }                                               
+                                       $origObjsArr = $bTos[$foreignTableRef];
+                                       $bTosString = implode(',', array_keys($bTos[$foreignTableRef]));
+                                       $foreignName = $foreignTable->foreignName;
+                                       $className = ucfirst($foreignTable->_singularize($foreignName));
+                                       $obj = new $className();
+                                       $objs = $obj->packageFind($belongsToId.' IN ('.$bTosString.')');
+                                       foreach($objs as $obj)
+                                       {
+                                               foreach($origObjsArr[$obj->$belongsToId] as $idx=>$origObj)
+                                               {
+                                                       $origObj->$foreignName = $obj;
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               return $arr;
+}
+?>
index 2872f2042d1155aa62a394f0a4288859290645a8..9c5385a747b311a0493b13084cac4c26c157e518 100644 (file)
@@ -8,7 +8,7 @@ $ADODB_INCLUDED_CSV = 1;
 
 /* 
 
-  V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
@@ -287,7 +287,7 @@ $ADODB_INCLUDED_CSV = 1;
                        fclose($fd);
                        
                        if ($ok) {
-                               chmod($tmpname,0644);
+                               @chmod($tmpname,0644);
                                // the tricky moment
                                @unlink($filename);
                                if (!@rename($tmpname,$filename)) {
@@ -305,7 +305,7 @@ $ADODB_INCLUDED_CSV = 1;
                        if (fwrite( $fd, $contents )) $ok = true;
                        else $ok = false;
                        fclose($fd);
-                       chmod($filename,0644);
+                       @chmod($filename,0644);
                }else {
                        fclose($fd);
                        if ($debug)ADOConnection::outp( " Failed acquiring lock for $filename<br>\n");
index d98d5511548fe7923616b9220bdaa89baaccc9af..c9ef4e7b64b657f1bea70ad3c05f1e8371a12a75 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /**
-  V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -230,6 +230,7 @@ class ADODB_DataDict {
                'CHARACTER' => 'C',
                'INTERVAL' => 'C',  # Postgres
                'MACADDR' => 'C', # postgres
+               'VAR_STRING' => 'C', # mysql
                ##
                'LONGCHAR' => 'X',
                'TEXT' => 'X',
@@ -257,6 +258,7 @@ class ADODB_DataDict {
                'TIMESTAMP' => 'T',
                'DATETIME' => 'T',
                'TIMESTAMPTZ' => 'T',
+               'SMALLDATETIME' => 'T',
                'T' => 'T',
                'TIMESTAMP WITHOUT TIME ZONE' => 'T', // postgresql
                ##
@@ -812,7 +814,7 @@ class ADODB_DataDict {
        
        
        // return string must begin with space
-       function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint)
+       function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
        {       
                $suffix = '';
                if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault";
@@ -917,7 +919,7 @@ class ADODB_DataDict {
        This function changes/adds new fields to your table. You don't
        have to know if the col is new or not. It will check on its own.
        */
-       function ChangeTableSQL($tablename, $flds, $tableoptions = false)
+       function ChangeTableSQL($tablename, $flds, $tableoptions = false, $dropOldFlds=false)
        {
        global $ADODB_FETCH_MODE;
        
@@ -950,13 +952,15 @@ class ADODB_DataDict {
                                        $obj = $cols[$k];
                                        if (isset($obj->not_null) && $obj->not_null)
                                                $v = str_replace('NOT NULL','',$v);
-
+                                       if (isset($obj->auto_increment) && $obj->auto_increment && empty($v['AUTOINCREMENT'])) 
+                                           $v = str_replace('AUTOINCREMENT','',$v);
+                                       
                                        $c = $cols[$k];
                                        $ml = $c->max_length;
                                        $mt = $this->MetaType($c->type,$ml);
                                        if ($ml == -1) $ml = '';
                                        if ($mt == 'X') $ml = $v['SIZE'];
-                                       if (($mt != $v['TYPE']) ||  $ml != $v['SIZE']) {
+                                       if (($mt != $v['TYPE']) ||  $ml != $v['SIZE'] || (isset($v['AUTOINCREMENT']) && $v['AUTOINCREMENT'] != $obj->auto_increment)) {
                                                $holdflds[$k] = $v;
                                        }
                                } else {
@@ -993,6 +997,11 @@ class ADODB_DataDict {
                        }
                }
                
+               if ($dropOldFlds) {
+                       foreach ( $cols as $id => $v )
+                           if ( !isset($lines[$id]) ) 
+                                       $sql[] = $alter . $this->dropCol . ' ' . $v->name;
+               }
                return $sql;
        }
 } // class
index 72779ab2cba6665f5c03fbbe4a923f3a350b545e..ae0b9e3543da50d0f082d02bd0b15848362c3f34 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /** 
- * @version V5.04a 25 Mar 2008  (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ * @version V5.06 16 Oct 2008  (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
index 4b7941fd0243bfb4cb26139c05d60f6453c62a09..cc735f8ec9719c32439abda1ba3367276c29319e 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * @version V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ * @version V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
index 70c2f6e0beb75fb9ffd69e5b336ea6ec921170f9..d1e2eb98f5e86b68cc486eb116b30e71ed8bf623 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /** 
- * @version V5.04a 25 Mar 2008  (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ * @version V5.06 16 Oct 2008  (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
index d2f589b8d07a62d065fe609f89a2b964818b2e17..a0b1b7704f39db0021111926985e66abc5c29eaf 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * @version V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ * @version V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
index 73e89047f9880d08579933689d8611dbe4b2b2c9..50d9d2aab170758bce77efccbc29b52b53de3143 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /*
-  V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
index 899972ea20a2fe86e51da19eb7891188f6055e20..5dd86a938905f5d576af74d59927e030901730a8 100644 (file)
@@ -10,7 +10,7 @@ global $ADODB_INCLUDED_LIB;
 $ADODB_INCLUDED_LIB = 1;
 
 /* 
- @version V5.04a 25 Mar 2008  (c) 2000-2008 John Lim (jlim\@natsoft.com.my). All rights reserved.
+ @version V5.06 16 Oct 2008  (c) 2000-2009 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. 
@@ -147,7 +147,7 @@ function _adodb_replace(&$zthis, $table, $fieldArray, $keyCol, $autoQuote, $has_
                        if ($v === null) {
                                $v = 'NULL';
                                $fieldArray[$k] = $v;
-                       } else if ($autoQuote && !is_numeric($v) /*and strncmp($v,"'",1) !== 0 -- sql injection risk*/ and strcasecmp($v,$zthis->null2null)!=0) {
+                       } else if ($autoQuote && /*!is_numeric($v) /*and strncmp($v,"'",1) !== 0 -- sql injection risk*/ strcasecmp($v,$zthis->null2null)!=0) {
                                $v = $zthis->qstr($v);
                                $fieldArray[$k] = $v;
                        }
@@ -159,7 +159,7 @@ function _adodb_replace(&$zthis, $table, $fieldArray, $keyCol, $autoQuote, $has_
                        } else
                                $uSet .= ",$k=$v";
                }
-                
+               
                $where = false;
                foreach ($keyCol as $v) {
                        if (isset($fieldArray[$v])) {
@@ -542,7 +542,7 @@ function _adodb_pageexecute_all_rows(&$zthis, $sql, $nrows, $page,
        return $rsreturn;
 }
 
-// Ivn Oliva version
+// Iván Oliva version
 function _adodb_pageexecute_no_last_page(&$zthis, $sql, $nrows, $page, $inputarr=false, $secs2cache=0) 
 {
 
@@ -999,15 +999,15 @@ function _adodb_column_sql(&$zthis, $action, $type, $fname, $fnameq, $arrFields,
 
                case "T":
                        $val = $zthis->DBTimeStamp($arrFields[$fname]);
-            break;
-
-        case "F": //Floating point number       // Moodle added
+                       break;
+                       
+               case "F": //Floating point number       // Moodle added
                case "N":
                    $val = $arrFields[$fname];
                        if (!is_numeric($val)) $val = str_replace(',', '.', (float)$val);
                    break;
 
-        case "L": //Integer field suitable for storing booleans (0 or 1)     // Moodle added
+               case "L": //Integer field suitable for storing booleans (0 or 1)     // Moodle added
                case "I":
                case "R":
                    $val = $arrFields[$fname];
@@ -1055,11 +1055,13 @@ function _adodb_debug_execute(&$zthis, $sql, $inputarr)
                        $ss = '<code>'.htmlspecialchars($ss).'</code>';
                }
                if ($zthis->debug === -1)
-                       ADOConnection::outp( "<br />\n($dbt): ".htmlspecialchars($sqlTxt)." &nbsp; $ss\n<br />\n",false);
-               else 
-                       ADOConnection::outp( "<hr />\n($dbt): ".htmlspecialchars($sqlTxt)." &nbsp; $ss\n<hr />\n",false);
+                       ADOConnection::outp( "<br />\n($dbt): ".htmlspecialchars($sqlTxt)." &nbsp; $ss\n<br />\n",false); /// Moodle XHTML
+               else if ($zthis->debug !== -99)
+                       ADOConnection::outp( "<hr />\n($dbt): ".htmlspecialchars($sqlTxt)." &nbsp; $ss\n<hr />\n",false); /// Moodle XHTML
        } else {
-               ADOConnection::outp("-----\n($dbt): ".$sqlTxt."\n-----\n",false);
+               $ss = "\n   ".$ss;
+               if ($zthis->debug !== -99)
+                       ADOConnection::outp("-----<hr />\n($dbt): ".$sqlTxt." $ss\n-----<hr />\n",false); /// Moodle XHTML
        }
 
        $qID = $zthis->_query($sql,$inputarr);
@@ -1070,10 +1072,21 @@ function _adodb_debug_execute(&$zthis, $sql, $inputarr)
        */
        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);
+                       if ($err = $zthis->ErrorNo()) {
+                               if ($zthis->debug === -99) 
+                                       ADOConnection::outp( "<hr />\n($dbt): ".htmlspecialchars($sqlTxt)." &nbsp; $ss\n<hr />\n",false); /// Moodle XHTML
+               
+                               ADOConnection::outp($err.': '.$emsg);
+                       }
                }
        } else if (!$qID) {
+       
+               if ($zthis->debug === -99) 
+                               if ($inBrowser) ADOConnection::outp( "<hr />\n($dbt): ".htmlspecialchars($sqlTxt)." &nbsp; $ss\n<hr />\n",false); /// Moodle XHTML
+                               else ADOConnection::outp("-----<hr />\n($dbt): ".$sqlTxt."$ss\n-----<hr />\n",false);
+                               
                ADOConnection::outp($zthis->ErrorNo() .': '. $zthis->ErrorMsg());
        }
        
@@ -1082,20 +1095,18 @@ function _adodb_debug_execute(&$zthis, $sql, $inputarr)
 }
 
 # pretty print the debug_backtrace function
-function _adodb_backtrace($printOrArr=true,$levels=9999,$skippy=0)
+function _adodb_backtrace($printOrArr=true,$levels=9999,$skippy=0,$ishtml=null)
 {
        if (!function_exists('debug_backtrace')) return '';
         
-       $html =  (isset($_SERVER['HTTP_USER_AGENT']));
-// moodle change start - see readme_moodle.txt
-       $fmt =  ($html) ? "</font><font color=\"#808080\" size=\"-1\"> %% line %4d, file: <a href=\"file:/%s\">%s</a></font>" : "%% line %4d, file: %s";
-// moodle change end
+       if ($ishtml === null) $html =  (isset($_SERVER['HTTP_USER_AGENT']));
+       else $html = $ishtml;
+       
+       $fmt =  ($html) ? "</font><font color=#808080 size=-1> %% line %4d, file: <a href=\"file:/%s\">%s</a></font>" : "%% line %4d, file: %s";
 
        $MAXSTRLEN = 128;
 
-// moodle change start - see readme_moodle.txt
-       $s = ($html) ? '<pre align="left">' : '';
-// moodle change end
+       $s = ($html) ? '<pre align=left>' : '';
        
        if (is_array($printOrArr)) $traceArr = $printOrArr;
        else $traceArr = debug_backtrace();
@@ -1121,7 +1132,7 @@ function _adodb_backtrace($printOrArr=true,$levels=9999,$skippy=0)
                        else if (is_bool($v)) $args[] = $v ? 'true' : 'false';
                        else {
                                $v = (string) @$v;
-                               $str = htmlspecialchars(substr($v,0,$MAXSTRLEN));
+                               $str = htmlspecialchars(str_replace(array("\r","\n"),' ',substr($v,0,$MAXSTRLEN)));
                                if (strlen($v) > $MAXSTRLEN) $str .= '...';
                                $args[] = $str;
                        }
@@ -1181,4 +1192,4 @@ function _adodb_find_from($sql)
 }
 */
 
-?>
\ No newline at end of file
+?>
index a4b7e7531c7710a789bf198a18e8e5ffa340ecf1..f50dbdb942f1fb5b567817950f573f4da2a8a1cc 100644 (file)
@@ -6,113 +6,185 @@ if (!defined('ADODB_DIR')) die();
 global $ADODB_INCLUDED_MEMCACHE;
 $ADODB_INCLUDED_MEMCACHE = 1;
 
+global $ADODB_INCLUDED_CSV;
+if (empty($ADODB_INCLUDED_CSV)) include(ADODB_DIR.'/adodb-csvlib.inc.php');
+
 /* 
 
-  V4.90 8 June 2006  (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.06 16 Oct 2008  (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
   
   Latest version is available at http://adodb.sourceforge.net
+
+Usage:
+  
+$db = NewADOConnection($driver);
+$db->memCache = true; /// should we use memCache instead of caching in files
+$db->memCacheHost = array($ip1, $ip2, $ip3);
+$db->memCachePort = 11211; /// this is default memCache port
+$db->memCacheCompress = false; /// Use 'true' to store the item compressed (uses zlib)
+
+$db->Connect(...);
+$db->CacheExecute($sql);
+  
+  Note the memcache class is shared by all connections, is created during the first call to Connect/PConnect.
   
+  Class instance is stored in $ADODB_CACHE
 */
 
-       function getmemcache($key,&$err, $timeout=0, $host, $port)
-       {
-               $false = false;
-               $err = false;
-
-               if (!function_exists('memcache_pconnect')) {
-                       $err = 'Memcache module PECL extension not found!';
-                       return $false;
+       class ADODB_Cache_MemCache {
+               var $createdir = false; // create caching directory structure?
+               
+               //-----------------------------
+               // memcache specific variables
+               
+               var $hosts;     // array of hosts
+               var $port = 11211;
+               var $compress = false; // memcache compression with zlib
+               
+               var $_connected = false;
+               var $_memcache = false;
+               
+               function ADODB_Cache_MemCache(&$obj)
+               {
+                       $this->hosts = $obj->memCacheHost;
+                       $this->port = $obj->memCachePort;
+                       $this->compress = $obj->memCacheCompress;
                }
+               
+               // implement as lazy connection. The connection only occurs on CacheExecute call
+               function connect(&$err)
+               {
+                       if (!function_exists('memcache_pconnect')) {
+                               $err = 'Memcache module PECL extension not found!';
+                               return false;
+                       }
 
-               $memcache = new Memcache;
-               if (!@$memcache->pconnect($host, $port)) {
-                       $err = 'Can\'t connect to memcache server on: '.$host.':'.$port;
-                       return $false;
+                       $memcache = new MemCache;
+                       
+                       if (!is_array($this->hosts)) $this->hosts = array($hosts);
+               
+                       $failcnt = 0;
+                       foreach($this->hosts as $host) {
+                               if (!@$memcache->addServer($host,$this->port,true)) {
+                                       $failcnt += 1;
+                               }
+                       }
+                       if ($failcnt == sizeof($this->hosts)) {
+                               $err = 'Can\'t connect to any memcache server';
+                               return false;
+                       }
+                       $this->_connected = true;
+                       $this->_memcache = $memcache;
+                       return true;
                }
-
-               $rs = $memcache->get($key);
-               if (!$rs) {
-                       $err = 'Item with such key doesn\'t exists on the memcached server.';
-                       return $false;
+               
+               // returns true or false. true if successful save
+               function writecache($filename, $contents, $debug, $secs2cache)
+               {
+                       if (!$this->_connected) {
+                               $err = '';
+                               if (!$this->connect($err) && $debug) ADOConnection::outp($err);
+                       }
+                       if (!$this->_memcache) return false;
+                       
+                       if (!$this->_memcache->set($filename, $contents, $this->compress, $secs2cache)) {
+                               if ($debug) ADOConnection::outp(" Failed to save data at the memcached server!<br>\n");
+                               return false;
+                       }
+                       
+                       return true;
                }
-
-               $tdiff = intval($rs->timeCreated+$timeout - time());
-               if ($tdiff <= 2) {
-                       switch($tdiff) {
-                               case 2: 
-                                       if ((rand() & 15) == 0) {
-                                               $err = "Timeout 2";
-                                               return $false;
-                                       }
-                                       break;
-                               case 1:
-                                       if ((rand() & 3) == 0) {
-                                               $err = "Timeout 1";
+               
+               // returns a recordset
+               function readcache($filename, &$err, $secs2cache, $rsClass)
+               {
+                       $false = false;
+                       if (!$this->_connected) $this->connect($err);
+                       if (!$this->_memcache) return $false;
+                       
+                       $rs = $this->_memcache->get($filename);
+                       if (!$rs) {
+                               $err = 'Item with such key doesn\'t exists on the memcached server.';
+                               return $false;
+                       }
+                       
+                       // hack, should actually use _csv2rs
+                       $rs = explode("\n", $rs);
+            unset($rs[0]);
+            $rs = join("\n", $rs);
+                       $rs = unserialize($rs);
+                       if (! is_object($rs)) {
+                               $err = 'Unable to unserialize $rs';             
+                               return $false;
+                       }
+                       if ($rs->timeCreated == 0) return $rs; // apparently have been reports that timeCreated was set to 0 somewhere
+                       
+                       $tdiff = intval($rs->timeCreated+$secs2cache - time());
+                       if ($tdiff <= 2) {
+                               switch($tdiff) {
+                                       case 2: 
+                                               if ((rand() & 15) == 0) {
+                                                       $err = "Timeout 2";
+                                                       return $false;
+                                               }
+                                               break;
+                                       case 1:
+                                               if ((rand() & 3) == 0) {
+                                                       $err = "Timeout 1";
+                                                       return $false;
+                                               }
+                                               break;
+                                       default: 
+                                               $err = "Timeout 0";
                                                return $false;
-                                       }
-                                       break;
-                               default: 
-                                       $err = "Timeout 0";
-                                       return $false;
+                               }
                        }
+                       return $rs;
                }
-               return $rs;
-       }
-
-       function putmemcache($key, $rs, $host, $port, $compress, $debug=false)
-       {
-               $false = false;
-               $true = true;
-
-               if (!function_exists('memcache_pconnect')) {
-                       if ($debug) ADOConnection::outp(" Memcache module PECL extension not found!<br>\n");
-                       return $false;
+               
+               function flushall($debug=false)
+               {
+                       if (!$this->_connected) {
+                               $err = '';
+                               if (!$this->connect($err) && $debug) ADOConnection::outp($err);
+                       }
+                       if (!$this->_memcache) return false;
+                       
+                       $del = $this->_memcache->flush();
+                       
+                       if ($debug) 
+                               if (!$del) ADOConnection::outp("flushall: failed!<br>\n");
+                               else ADOConnection::outp("flushall: succeeded!<br>\n");
+                               
+                       return $del;
                }
-
-               $memcache = new Memcache;
-               if (!@$memcache->pconnect($host, $port)) {
-                       if ($debug) ADOConnection::outp(" Can't connect to memcache server on: $host:$port<br>\n");
-                       return $false;
+               
+               function flushcache($filename, $debug=false)
+               {
+                       if (!$this->_connected) {
+                               $err = '';
+                               if (!$this->connect($err) && $debug) ADOConnection::outp($err); 
+                       } 
+                       if (!$this->_memcache) return false;
+  
+                       $del = $this->_memcache->delete($filename);
+                       
+                       if ($debug) 
+                               if (!$del) ADOConnection::outp("flushcache: $key entry doesn't exist on memcached server!<br>\n");
+                               else ADOConnection::outp("flushcache: $key entry flushed from memcached server!<br>\n");
+                               
+                       return $del;
                }
-
-               $rs->timeCreated = time();
-               if (!$memcache->set($key, $rs, $compress, 0)) {
-                       if ($debug) ADOConnection::outp(" Failed to save data at the memcached server!<br>\n");
-                       return $false;
+               
+               // not used for memcache
+               function createdir($dir, $hash) 
+               {
+                       return true;
                }
-               return $true;
        }
 
-       function flushmemcache($key=false, $host, $port, $debug=false)
-       {
-               if (!function_exists('memcache_pconnect')) {
-                       if ($debug) ADOConnection::outp(" Memcache module PECL extension not found!<br>\n");
-                       return;
-               }
-
-               $memcache = new Memcache;
-               if (!@$memcache->pconnect($host, $port)) {
-                       if ($debug) ADOConnection::outp(" Can't connect to memcache server on: $host:$port<br>\n");
-                       return;
-               }
-
-               if ($key) {
-                       if (!$memcache->delete($key)) {
-                               if ($debug) ADOConnection::outp("CacheFlush: $key entery doesn't exist on memcached server!<br>\n");
-                       } else {
-                               if ($debug) ADOConnection::outp("CacheFlush: $key entery flushed from memcached server!<br>\n");
-                       }
-               } else {
-                       if (!$memcache->flush()) {
-                               if ($debug) ADOConnection::outp("CacheFlush: Failure flushing all enteries from memcached server!<br>\n");
-                       } else {
-                               if ($debug) ADOConnection::outp("CacheFlush: All enteries flushed from memcached server!<br>\n");
-                       }
-               }
-               return;
-       }
-?>
+?>
\ No newline at end of file
index 16acee365f0cb9442e028e982e55b524dd043f3f..f542c3ccb672c522f4c6ec3953a2af1707db0006 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /*
-       V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+       V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
@@ -60,7 +60,7 @@ class ADODB_Pager {
        global $PHP_SELF;
        
                $curr_page = $id.'_curr_page';
-               if (empty($PHP_SELF)) $PHP_SELF = htmlspecialchars($_SERVER['PHP_SELF']); // htmlspecialchars() to prevent XSS attacks
+               if (!empty($PHP_SELF)) $PHP_SELF = htmlspecialchars($_SERVER['PHP_SELF']); // htmlspecialchars() to prevent XSS attacks
                
                $this->sql = $sql;
                $this->id = $id;
index 60696c5ccaad9133175aa68d5da7fa18d56d91b5..65637045581a493a667689c70e650f3066d482a5 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /** 
- * @version V5.04a 25 Mar 2008  (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ * @version V5.06 16 Oct 2008  (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
@@ -211,7 +211,7 @@ class DB
        function isError($value)
        {
                if (!is_object($value)) return false;
-               $class = get_class($value);
+               $class = strtolower(get_class($value));
                return $class == 'pear_error' || is_subclass_of($value, 'pear_error') || 
                                $class == 'db_error' || is_subclass_of($value, 'db_error');
        }
index 5f0e0a487543ce896dcc1f08807384f665066d19..cb878590521afcb6e3de0f56f9201a12c7bb6835 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
@@ -70,9 +70,9 @@ function adodb_log_sql(&$connx,$sql,$inputarr)
 {
     $perf_table = adodb_perf::table();
        $connx->fnExecute = false;
-       $t0 = microtime();
+       $a0 = microtime(true);
        $rs = $connx->Execute($sql,$inputarr);
-       $t1 = microtime();
+       $a1 = microtime(true);
 
        if (!empty($connx->_logsql) && (empty($connx->_logsqlErrors) || !$rs)) {
        global $ADODB_LOG_CONN;
@@ -91,12 +91,6 @@ function adodb_log_sql(&$connx,$sql,$inputarr)
                $conn->_logsql = false; // disable logsql error simulation
                $dbT = $conn->databaseType;
                
-               $a0 = split(' ',$t0);
-               $a0 = (float)$a0[1]+(float)$a0[0];
-               
-               $a1 = split(' ',$t1);
-               $a1 = (float)$a1[1]+(float)$a1[0];
-               
                $time = $a1 - $a0;
        
                if (!$rs) {
@@ -117,9 +111,9 @@ function adodb_log_sql(&$connx,$sql,$inputarr)
                }
                if (isset($_SERVER['HTTP_HOST'])) {
                        $tracer .= '<br>'.$_SERVER['HTTP_HOST'];
-                       if (isset($_SERVER['PHP_SELF'])) $tracer .= $_SERVER['PHP_SELF'];
+                       if (isset($_SERVER['PHP_SELF'])) $tracer .= htmlspecialchars($_SERVER['PHP_SELF']);
                } else 
-                       if (isset($_SERVER['PHP_SELF'])) $tracer .= '<br>'.$_SERVER['PHP_SELF'];
+                       if (isset($_SERVER['PHP_SELF'])) $tracer .= '<br>'.htmlspecialchars($_SERVER['PHP_SELF']);
                //$tracer .= (string) adodb_backtrace(false);
                
                $tracer = (string) substr($tracer,0,500);
@@ -264,7 +258,7 @@ processes 69293
 
 */
                // Algorithm is taken from
-               // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/example__obtaining_raw_performance_data.asp
+               // http://social.technet.microsoft.com/Forums/en-US/winservergen/thread/414b0e1b-499c-411e-8a02-6a12e339c0f1/
                if (strncmp(PHP_OS,'WIN',3)==0) {
                        if (PHP_VERSION == '5.0.0') return false;
                        if (PHP_VERSION == '5.0.1') return false;
@@ -272,14 +266,33 @@ processes 69293
                        if (PHP_VERSION == '5.0.3') return false;
                        if (PHP_VERSION == '4.3.10') return false; # see http://bugs.php.net/bug.php?id=31737
                        
-                       @$c = new COM("WinMgmts:{impersonationLevel=impersonate}!Win32_PerfRawData_PerfOS_Processor.Name='_Total'");
-                       if (!$c) return false;
+                       static $FAIL = false;
+                       if ($FAIL) return false;
+                       
+                       $objName = "winmgmts:{impersonationLevel=impersonate}!\\\\.\\root\\CIMV2";      
+                       $myQuery = "SELECT * FROM Win32_PerfFormattedData_PerfOS_Processor WHERE Name = '_Total'";
+                       
+                       try {
+                               @$objWMIService = new COM($objName);
+                               if (!$objWMIService) {
+                                       $FAIL = true;
+                                       return false;
+                               }
+               
+                               $info[0] = -1;
+                               $info[1] = 0;
+                               $info[2] = 0;
+                               $info[3] = 0;
+                               foreach($objWMIService->ExecQuery($myQuery) as $objItem)  {
+                                               $info[0] = $objItem->PercentProcessorTime();
+                               }
+                       
+                       } catch(Exception $e) {
+                               $FAIL = true;
+                               echo $e->getMessage();
+                               return false;
+                       }
                        
-                       $info[0] = $c->PercentProcessorTime;
-                       $info[1] = 0;
-                       $info[2] = 0;
-                       $info[3] = $c->TimeStamp_Sys100NS;
-                       //print_r($info);
                        return $info;
                }
                
@@ -342,27 +355,26 @@ Committed_AS:   348732 kB
        {
                $info = $this->_CPULoad();
                if (!$info) return false;
-                       
-               if (empty($this->_lastLoad)) {
-                       sleep(1);
-                       $this->_lastLoad = $info;
-                       $info = $this->_CPULoad();
-               }
-               
-               $last = $this->_lastLoad;
-               $this->_lastLoad = $info;
-               
-               $d_user = $info[0] - $last[0];
-               $d_nice = $info[1] - $last[1];
-               $d_system = $info[2] - $last[2];
-               $d_idle = $info[3] - $last[3];
                
-               //printf("Delta - User: %f  Nice: %f  System: %f  Idle: %f<br>",$d_user,$d_nice,$d_system,$d_idle);
-
                if (strncmp(PHP_OS,'WIN',3)==0) {
-                       if ($d_idle < 1) $d_idle = 1;
-                       return 100*(1-$d_user/$d_idle);
+                       return (integer) $info[0];
                }else {
+                       if (empty($this->_lastLoad)) {
+                               sleep(1);
+                               $this->_lastLoad = $info;
+                               $info = $this->_CPULoad();
+                       }
+                       
+                       $last = $this->_lastLoad;
+                       $this->_lastLoad = $info;
+                       
+                       $d_user = $info[0] - $last[0];
+                       $d_nice = $info[1] - $last[1];
+                       $d_system = $info[2] - $last[2];
+                       $d_idle = $info[3] - $last[3];
+                       
+                       //printf("Delta - User: %f  Nice: %f  System: %f  Idle: %f<br>",$d_user,$d_nice,$d_system,$d_idle);
+               
                        $total=$d_user+$d_nice+$d_system+$d_idle;
                        if ($total<1) $total=1;
                        return 100*($d_user+$d_nice+$d_system)/$total; 
@@ -727,8 +739,9 @@ Committed_AS:   348732 kB
                        echo $this->CheckMemory();              
                        break;
                case 'poll':
+                       $self = htmlspecialchars($_SERVER['PHP_SELF']);
                        echo "<iframe width=720 height=80% 
-                               src=\"{$_SERVER['PHP_SELF']}?do=poll2&hidem=1\"></iframe>";
+                               src=\"{$self}?do=poll2&hidem=1\"></iframe>";
                        break;
                case 'poll2':
                        echo "<pre>";
@@ -751,7 +764,7 @@ Committed_AS:   348732 kB
                        echo $this->Tables(); break;
                }
                global $ADODB_vers;
-               echo "<p><div class='mdl-align'><font size=1>$ADODB_vers Sponsored by <a href=http://phplens.com/>phpLens</a></font></div>";
+               echo "<p><div align=center><font size=1>$ADODB_vers Sponsored by <a href=http://phplens.com/>phpLens</a></font></div>";
        }
        
        /*
@@ -905,7 +918,7 @@ Committed_AS:   348732 kB
        {
        
                
-               $PHP_SELF = $_SERVER['PHP_SELF'];
+               $PHP_SELF = htmlspecialchars($_SERVER['PHP_SELF']);
                $sql = isset($_REQUEST['sql']) ? $_REQUEST['sql'] : '';
 
                if (isset($_SESSION['phplens_sqlrows'])) $rows = $_SESSION['phplens_sqlrows'];
@@ -1085,4 +1098,4 @@ Committed_AS:   348732 kB
     // end hack 
 }
 
-?>
+?>
\ No newline at end of file
index dca76105cba403bed01d8f693a7ccc2b25036198..9d84ded8a2ab59fc0341b802121acdfe473c15d9 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /*
-  V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
index 19b22fb4feb704ac624eef24cbab174b6addfaba..063742163b4859e4ed9e26f77539cbcd3d1b415e 100644 (file)
@@ -1006,7 +1006,6 @@ function adodb_tz_offset($gmt,$isphp5)
                return sprintf('%s%02d%02d',($gmt<=0)?'+':'-',floor($zhrs),($zhrs-$hrs)*60); 
        else
                return sprintf('%s%02d%02d',($gmt<0)?'+':'-',floor($zhrs),($zhrs-$hrs)*60); 
-       break;
 }
 
 
index 38490d7593cd271c67c9c6975fad76249956a0da..92c81afe7512d85ae23c0fcf3ad76bd0d41e4e6b 100644 (file)
-<?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$\r
- * @author Richard Tango-Lowy & Dan Cech\r
- * @version $Revision$\r
- *\r
- * @package axmls\r
- * @tutorial getting_started.pkg\r
- */\r
\r
-function _file_get_contents($file) \r
-{\r
-       if (function_exists('file_get_contents')) return file_get_contents($file);\r
-       \r
-       $f = fopen($file,'r');\r
-       if (!$f) return '';\r
-       $t = '';\r
-       \r
-       while ($s = fread($f,100000)) $t .= $s;\r
-       fclose($f);\r
-       return $t;\r
-}\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$\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
-                       $xmlstring = '';\r
-                       while( $data = fread( $fp, 100000 ) ) {\r
-                               $xmlstring .= $data;\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
-               $sqlArray = null;\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
-       // compat for pre-4.3 - jlim\r
-       function _file_get_contents($path)\r
-       {\r
-               if (function_exists('file_get_contents')) return file_get_contents($path);\r
-               return join('',file($path));\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
-                                               \r
-                                               if( !empty( $content ) ) {\r
-                                                       $schema .= "\n                  " . implode( "\n                        ", $content ) . "\n             ";\r
-                                               }\r
-                                               \r
-                                               $schema .= '</field>' . "\n";\r
-                                       }\r
-                               }\r
-                               \r
-                               if( is_array( $indexes ) ) {\r
-                                       foreach( $indexes as $index => $details ) {\r
-                                               $schema .= '            <index name="' . $index . '">' . "\n";\r
-                                               \r
-                                               if( $details['unique'] ) {\r
-                                                       $schema .= '                    <UNIQUE/>' . "\n";\r
-                                               }\r
-                                               \r
-                                               foreach( $details['columns'] as $column ) {\r
-                                                       $schema .= '                    <col>' . $column . '</col>' . "\n";\r
-                                               }\r
-                                               \r
-                                               $schema .= '            </index>' . "\n";\r
-                                       }\r
-                               }\r
-                               \r
-                               if( $data ) {\r
-                                       $rs = $this->db->Execute( 'SELECT * FROM ' . $table );\r
-                                       \r
-                                       if( is_object( $rs ) ) {\r
-                                               $schema .= '            <data>' . "\n";\r
-                                               \r
-                                               while( $row = $rs->FetchRow() ) {\r
-                                                       foreach( $row as $key => $val ) {\r
-                                                               $row[$key] = htmlentities($val);\r
-                                                       }\r
-                                                       \r
-                                                       $schema .= '                    <row><f>' . implode( '</f><f>', $row ) . '</f></row>' . "\n";\r
-                                               }\r
-                                               \r
-                                               $schema .= '            </data>' . "\n";\r
-                                       }\r
-                               }\r
-                               \r
-                               $schema .= '    </table>' . "\n";\r
-                       }\r
-               }\r
-               \r
-               $this->db->SetFetchMode( $old_mode );\r
-               \r
-               $schema .= '</schema>';\r
-               return $schema;\r
-       }\r
-       \r
-       /**\r
-       * Sets a prefix for database objects\r
-       *\r
-       * Call this method to set a standard prefix that will be prepended to all database tables \r
-       * and indices when the schema is parsed. Calling setPrefix with no arguments clears the prefix.\r
-       *\r
-       * @param string $prefix Prefix that will be prepended.\r
-       * @param boolean $underscore If TRUE, automatically append an underscore character to the prefix.\r
-       * @return boolean TRUE if successful, else FALSE\r
-       */\r
-       function SetPrefix( $prefix = '', $underscore = TRUE ) {\r
-               switch( TRUE ) {\r
-                       // clear prefix\r
-                       case empty( $prefix ):\r
-                               logMsg( 'Cleared prefix' );\r
-                               $this->objectPrefix = '';\r
-                               return TRUE;\r
-                       // prefix too long\r
-                       case strlen( $prefix ) > XMLS_PREFIX_MAXLEN:\r
-                       // prefix contains invalid characters\r
-                       case !preg_match( '/^[a-z][a-z0-9_]+$/i', $prefix ):\r
-                               logMsg( 'Invalid prefix: ' . $prefix );\r
-                               return FALSE;\r
-               }\r
-               \r
-               if( $underscore AND substr( $prefix, -1 ) != '_' ) {\r
-                       $prefix .= '_';\r
-               }\r
-               \r
-               // prefix valid\r
-               logMsg( 'Set prefix: ' . $prefix );\r
-               $this->objectPrefix = $prefix;\r
-               return TRUE;\r
-       }\r
-       \r
-       /**\r
-       * Returns an object name with the current prefix prepended.\r
-       *\r
-       * @param string $name Name\r
-       * @return string        Prefixed name\r
-       *\r
-       * @access private\r
-       */\r
-       function prefix( $name = '' ) {\r
-               // if prefix is set\r
-               if( !empty( $this->objectPrefix ) ) {\r
-                       // Prepend the object prefix to the table name\r
-                       // prepend after quote if used\r
-                       return preg_replace( '/^(`?)(.+)$/', '$1' . $this->objectPrefix . '$2', $name );\r
-               }\r
-               \r
-               // No prefix set. Use name provided.\r
-               return $name;\r
-       }\r
-       \r
-       /**\r
-       * Checks if element references a specific platform\r
-       *\r
-       * @param string $platform Requested platform\r
-       * @returns boolean TRUE if platform check succeeds\r
-       *\r
-       * @access private\r
-       */\r
-       function supportedPlatform( $platform = NULL ) {\r
-               $regex = '/^(\w*\|)*' . $this->db->databaseType . '(\|\w*)*$/';\r
-               \r
-               if( !isset( $platform ) OR preg_match( $regex, $platform ) ) {\r
-                       logMsg( "Platform $platform is supported" );\r
-                       return TRUE;\r
-               } else {\r
-                       logMsg( "Platform $platform is NOT supported" );\r
-                       return FALSE;\r
-               }\r
-       }\r
-       \r
-       /**\r
-       * Clears the array of generated SQL.\r
-       *\r
-       * @access private\r
-       */\r
-       function clearSQL() {\r
-               $this->sqlArray = array();\r
-       }\r
-       \r
-       /**\r
-       * Adds SQL into the SQL array.\r
-       *\r
-       * @param mixed $sql SQL to Add\r
-       * @return boolean TRUE if successful, else FALSE.\r
-       *\r
-       * @access private\r
-       */      \r
-       function addSQL( $sql = NULL ) {\r
-               if( is_array( $sql ) ) {\r
-                       foreach( $sql as $line ) {\r
-                               $this->addSQL( $line );\r
-                       }\r
-                       \r
-                       return TRUE;\r
-               }\r
-               \r
-               if( is_string( $sql ) ) {\r
-                       $this->sqlArray[] = $sql;\r
-                       \r
-                       // if executeInline is enabled, and either no errors have occurred or continueOnError is enabled, execute SQL.\r
-                       if( $this->ExecuteInline() && ( $this->success == 2 || $this->ContinueOnError() ) ) {\r
-                               $saved = $this->db->debug;\r
-                               $this->db->debug = $this->debug;\r
-                               $ok = $this->db->Execute( $sql );\r
-                               $this->db->debug = $saved;\r
-                               \r
-                               if( !$ok ) {\r
-                                       if( $this->debug ) {\r
-                                               ADOConnection::outp( $this->db->ErrorMsg() );\r
-                                       }\r
-                                       \r
-                                       $this->success = 1;\r
-                               }\r
-                       }\r
-                       \r
-                       return TRUE;\r
-               }\r
-               \r
-               return FALSE;\r
-       }\r
-       \r
-       /**\r
-       * Gets the SQL array in the specified format.\r
-       *\r
-       * @param string $format Format\r
-       * @return mixed SQL\r
-       *       \r
-       * @access private\r
-       */\r
-       function getSQL( $format = NULL, $sqlArray = NULL ) {\r
-               if( !is_array( $sqlArray ) ) {\r
-                       $sqlArray = $this->sqlArray;\r
-               }\r
-               \r
-               if( !is_array( $sqlArray ) ) {\r
-                       return FALSE;\r
-               }\r
-               \r
-               switch( strtolower( $format ) ) {\r
-                       case 'string':\r
-                       case 'text':\r
-                               return !empty( $sqlArray ) ? implode( ";\n\n", $sqlArray ) . ';' : '';\r
-                       case'html':\r
-                               return !empty( $sqlArray ) ? nl2br( htmlentities( implode( ";\n\n", $sqlArray ) . ';' ) ) : '';\r
-               }\r
-               \r
-               return $this->sqlArray;\r
-       }\r
-       \r
-       /**\r
-       * Destroys an adoSchema object.\r
-       *\r
-       * Call this method to clean up after an adoSchema object that is no longer in use.\r
-       * @deprecated adoSchema now cleans up automatically.\r
-       */\r
-       function Destroy() {\r
-               set_magic_quotes_runtime( $this->mgq );\r
-               unset( $this );\r
-       }\r
-}\r
-\r
-/**\r
-* Message logging function\r
-*\r
-* @access private\r
-*/\r
-function logMsg( $msg, $title = NULL, $force = FALSE ) {\r
-       if( XMLS_DEBUG or $force ) {\r
-               echo '<pre>';\r
-               \r
-               if( isset( $title ) ) {\r
-                       echo '<h3>' . htmlentities( $title ) . '</h3>';\r
-               }\r
-               \r
-               if( is_object( $this ) ) {\r
-                       echo '[' . get_class( $this ) . '] ';\r
-               }\r
-               \r
-               print_r( $msg );\r
-               \r
-               echo '</pre>';\r
-       }\r
-}\r
+<?php
+// Copyright (c) 2004 ars Cognita Inc., 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. 
+*******************************************************************************/
+/**
+ * xmlschema is a class that allows the user to quickly and easily
+ * build a database on any ADOdb-supported platform using a simple
+ * XML schema.
+ *
+ * Last Editor: $Author$
+ * @author Richard Tango-Lowy & Dan Cech
+ * @version $Revision$
+ *
+ * @package axmls
+ * @tutorial getting_started.pkg
+ */
+function _file_get_contents($file) 
+{
+       if (function_exists('file_get_contents')) return file_get_contents($file);
+       
+       $f = fopen($file,'r');
+       if (!$f) return '';
+       $t = '';
+       
+       while ($s = fread($f,100000)) $t .= $s;
+       fclose($f);
+       return $t;
+}
+
+
+/**
+* Debug on or off
+*/
+if( !defined( 'XMLS_DEBUG' ) ) {
+       define( 'XMLS_DEBUG', FALSE );
+}
+
+/**
+* Default prefix key
+*/
+if( !defined( 'XMLS_PREFIX' ) ) {
+       define( 'XMLS_PREFIX', '%%P' );
+}
+
+/**
+* Maximum length allowed for object prefix
+*/
+if( !defined( 'XMLS_PREFIX_MAXLEN' ) ) {
+       define( 'XMLS_PREFIX_MAXLEN', 10 );
+}
+
+/**
+* Execute SQL inline as it is generated
+*/
+if( !defined( 'XMLS_EXECUTE_INLINE' ) ) {
+       define( 'XMLS_EXECUTE_INLINE', FALSE );
+}
+
+/**
+* Continue SQL Execution if an error occurs?
+*/
+if( !defined( 'XMLS_CONTINUE_ON_ERROR' ) ) {
+       define( 'XMLS_CONTINUE_ON_ERROR', FALSE );
+}
+
+/**
+* Current Schema Version
+*/
+if( !defined( 'XMLS_SCHEMA_VERSION' ) ) {
+       define( 'XMLS_SCHEMA_VERSION', '0.2' );
+}
+
+/**
+* Default Schema Version.  Used for Schemas without an explicit version set.
+*/
+if( !defined( 'XMLS_DEFAULT_SCHEMA_VERSION' ) ) {
+       define( 'XMLS_DEFAULT_SCHEMA_VERSION', '0.1' );
+}
+
+/**
+* Default Schema Version.  Used for Schemas without an explicit version set.
+*/
+if( !defined( 'XMLS_DEFAULT_UPGRADE_METHOD' ) ) {
+       define( 'XMLS_DEFAULT_UPGRADE_METHOD', 'ALTER' );
+}
+
+/**
+* Include the main ADODB library
+*/
+if( !defined( '_ADODB_LAYER' ) ) {
+       require( 'adodb.inc.php' );
+       require( 'adodb-datadict.inc.php' );
+}
+
+/**
+* Abstract DB Object. This class provides basic methods for database objects, such
+* as tables and indexes.
+*
+* @package axmls
+* @access private
+*/
+class dbObject {
+       
+       /**
+       * var object Parent
+       */
+       var $parent;
+       
+       /**
+       * var string current element
+       */
+       var $currentElement;
+       
+       /**
+       * NOP
+       */
+       function dbObject( &$parent, $attributes = NULL ) {
+               $this->parent = $parent;
+       }
+       
+       /**
+       * XML Callback to process start elements
+       *
+       * @access private
+       */
+       function _tag_open( &$parser, $tag, $attributes ) {
+               
+       }
+       
+       /**
+       * XML Callback to process CDATA elements
+       *
+       * @access private
+       */
+       function _tag_cdata( &$parser, $cdata ) {
+               
+       }
+       
+       /**
+       * XML Callback to process end elements
+       *
+       * @access private
+       */
+       function _tag_close( &$parser, $tag ) {
+               
+       }
+       
+       function create() {
+               return array();
+       }
+       
+       /**
+       * Destroys the object
+       */
+       function destroy() {
+               unset( $this );
+       }
+       
+       /**
+       * Checks whether the specified RDBMS is supported by the current
+       * database object or its ranking ancestor.
+       *
+       * @param string $platform RDBMS platform name (from ADODB platform list).
+       * @return boolean TRUE if RDBMS is supported; otherwise returns FALSE.
+       */
+       function supportedPlatform( $platform = NULL ) {
+               return is_object( $this->parent ) ? $this->parent->supportedPlatform( $platform ) : TRUE;
+       }
+       
+       /**
+       * Returns the prefix set by the ranking ancestor of the database object.
+       *
+       * @param string $name Prefix string.
+       * @return string Prefix.
+       */
+       function prefix( $name = '' ) {
+               return is_object( $this->parent ) ? $this->parent->prefix( $name ) : $name;
+       }
+       
+       /**
+       * Extracts a field ID from the specified field.
+       *
+       * @param string $field Field.
+       * @return string Field ID.
+       */
+       function FieldID( $field ) {
+               return strtoupper( preg_replace( '/^`(.+)`$/', '$1', $field ) );
+       }
+}
+
+/**
+* Creates a table object in ADOdb's datadict format
+*
+* This class stores information about a database table. As charactaristics
+* of the table are loaded from the external source, methods and properties
+* of this class are used to build up the table description in ADOdb's
+* datadict format.
+*
+* @package axmls
+* @access private
+*/
+class dbTable extends dbObject {
+       
+       /**
+       * @var string Table name
+       */
+       var $name;
+       
+       /**
+       * @var array Field specifier: Meta-information about each field
+       */
+       var $fields = array();
+       
+       /**
+       * @var array List of table indexes.
+       */
+       var $indexes = array();
+       
+       /**
+       * @var array Table options: Table-level options
+       */
+       var $opts = array();
+       
+       /**
+       * @var string Field index: Keeps track of which field is currently being processed
+       */
+       var $current_field;
+       
+       /**
+       * @var boolean Mark table for destruction
+       * @access private
+       */
+       var $drop_table;
+       
+       /**
+       * @var boolean Mark field for destruction (not yet implemented)
+       * @access private
+       */
+       var $drop_field = array();
+       
+       /**
+       * Iniitializes a new table object.
+       *
+       * @param string $prefix DB Object prefix
+       * @param array $attributes Array of table attributes.
+       */
+       function dbTable( &$parent, $attributes = NULL ) {
+               $this->parent = $parent;
+               $this->name = $this->prefix($attributes['NAME']);
+       }
+       
+       /**
+       * XML Callback to process start elements. Elements currently 
+       * processed are: INDEX, DROP, FIELD, KEY, NOTNULL, AUTOINCREMENT & DEFAULT. 
+       *
+       * @access private
+       */
+       function _tag_open( &$parser, $tag, $attributes ) {
+               $this->currentElement = strtoupper( $tag );
+               
+               switch( $this->currentElement ) {
+                       case 'INDEX':
+                               if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
+                                       xml_set_object( $parser, $this->addIndex( $attributes ) );
+                               }
+                               break;
+                       case 'DATA':
+                               if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
+                                       xml_set_object( $parser, $this->addData( $attributes ) );
+                               }
+                               break;
+                       case 'DROP':
+                               $this->drop();
+                               break;
+                       case 'FIELD':
+                               // Add a field
+                               $fieldName = $attributes['NAME'];
+                               $fieldType = $attributes['TYPE'];
+                               $fieldSize = isset( $attributes['SIZE'] ) ? $attributes['SIZE'] : NULL;
+                               $fieldOpts = isset( $attributes['OPTS'] ) ? $attributes['OPTS'] : NULL;
+                               
+                               $this->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts );
+                               break;
+                       case 'KEY':
+                       case 'NOTNULL':
+                       case 'AUTOINCREMENT':
+                               // Add a field option
+                               $this->addFieldOpt( $this->current_field, $this->currentElement );
+                               break;
+                       case 'DEFAULT':
+                               // Add a field option to the table object
+                               
+                               // Work around ADOdb datadict issue that misinterprets empty strings.
+                               if( $attributes['VALUE'] == '' ) {
+                                       $attributes['VALUE'] = " '' ";
+                               }
+                               
+                               $this->addFieldOpt( $this->current_field, $this->currentElement, $attributes['VALUE'] );
+                               break;
+                       case 'DEFDATE':
+                       case 'DEFTIMESTAMP':
+                               // Add a field option to the table object
+                               $this->addFieldOpt( $this->current_field, $this->currentElement );
+                               break;
+                       default:
+                               // print_r( array( $tag, $attributes ) );
+               }
+       }
+       
+       /**
+       * XML Callback to process CDATA elements
+       *
+       * @access private
+       */
+       function _tag_cdata( &$parser, $cdata ) {
+               switch( $this->currentElement ) {
+                       // Table constraint
+                       case 'CONSTRAINT':
+                               if( isset( $this->current_field ) ) {
+                                       $this->addFieldOpt( $this->current_field, $this->currentElement, $cdata );
+                               } else {
+                                       $this->addTableOpt( $cdata );
+                               }
+                               break;
+                       // Table option
+                       case 'OPT':
+                               $this->addTableOpt( $cdata );
+                               break;
+                       default:
+                               
+               }
+       }
+       
+       /**
+       * XML Callback to process end elements
+       *
+       * @access private
+       */
+       function _tag_close( &$parser, $tag ) {
+               $this->currentElement = '';
+               
+               switch( strtoupper( $tag ) ) {
+                       case 'TABLE':
+                               $this->parent->addSQL( $this->create( $this->parent ) );
+                               xml_set_object( $parser, $this->parent );
+                               $this->destroy();
+                               break;
+                       case 'FIELD':
+                               unset($this->current_field);
+                               break;
+
+               }
+       }
+       
+       /**
+       * Adds an index to a table object
+       *
+       * @param array $attributes Index attributes
+       * @return object dbIndex object
+       */
+       function addIndex( $attributes ) {
+               $name = strtoupper( $attributes['NAME'] );
+               $this->indexes[$name] = new dbIndex( $this, $attributes );
+               return $this->indexes[$name];
+       }
+       
+       /**
+       * Adds data to a table object
+       *
+       * @param array $attributes Data attributes
+       * @return object dbData object
+       */
+       function addData( $attributes ) {
+               if( !isset( $this->data ) ) {
+                       $this->data = new dbData( $this, $attributes );
+               }
+               return $this->data;
+       }
+       
+       /**
+       * Adds a field to a table object
+       *
+       * $name is the name of the table to which the field should be added. 
+       * $type is an ADODB datadict field type. The following field types
+       * are supported as of ADODB 3.40:
+       *       - C:  varchar
+       *       - X:  CLOB (character large object) or largest varchar size
+       *          if CLOB is not supported
+       *       - C2: Multibyte varchar
+       *       - X2: Multibyte CLOB
+       *       - B:  BLOB (binary large object)
+       *       - D:  Date (some databases do not support this, and we return a datetime type)
+       *       - T:  Datetime or Timestamp
+       *       - L:  Integer field suitable for storing booleans (0 or 1)
+       *       - I:  Integer (mapped to I4)
+       *       - I1: 1-byte integer
+       *       - I2: 2-byte integer
+       *       - I4: 4-byte integer
+       *       - I8: 8-byte integer
+       *       - F:  Floating point number
+       *       - N:  Numeric or decimal number
+       *
+       * @param string $name Name of the table to which the field will be added.
+       * @param string $type   ADODB datadict field type.
+       * @param string $size   Field size
+       * @param array $opts    Field options array
+       * @return array Field specifier array
+       */
+       function addField( $name, $type, $size = NULL, $opts = NULL ) {
+               $field_id = $this->FieldID( $name );
+               
+               // Set the field index so we know where we are
+               $this->current_field = $field_id;
+               
+               // Set the field name (required)
+               $this->fields[$field_id]['NAME'] = $name;
+               
+               // Set the field type (required)
+               $this->fields[$field_id]['TYPE'] = $type;
+               
+               // Set the field size (optional)
+               if( isset( $size ) ) {
+                       $this->fields[$field_id]['SIZE'] = $size;
+               }
+               
+               // Set the field options
+               if( isset( $opts ) ) {
+                       $this->fields[$field_id]['OPTS'][] = $opts;
+               }
+       }
+       
+       /**
+       * Adds a field option to the current field specifier
+       *
+       * This method adds a field option allowed by the ADOdb datadict 
+       * and appends it to the given field.
+       *
+       * @param string $field  Field name
+       * @param string $opt ADOdb field option
+       * @param mixed $value Field option value
+       * @return array Field specifier array
+       */
+       function addFieldOpt( $field, $opt, $value = NULL ) {
+               if( !isset( $value ) ) {
+                       $this->fields[$this->FieldID( $field )]['OPTS'][] = $opt;
+               // Add the option and value
+               } else {
+                       $this->fields[$this->FieldID( $field )]['OPTS'][] = array( $opt => $value );
+               }
+       }
+       
+       /**
+       * Adds an option to the table
+       *
+       * This method takes a comma-separated list of table-level options
+       * and appends them to the table object.
+       *
+       * @param string $opt Table option
+       * @return array Options
+       */
+       function addTableOpt( $opt ) {
+               if(isset($this->currentPlatform)) {
+                       $this->opts[$this->parent->db->databaseType] = $opt;
+               }
+               return $this->opts;
+       }
+
+       
+       /**
+       * Generates the SQL that will create the table in the database
+       *
+       * @param object $xmls adoSchema object
+       * @return array Array containing table creation SQL
+       */
+       function create( &$xmls ) {
+               $sql = array();
+               
+               // drop any existing indexes
+               if( is_array( $legacy_indexes = $xmls->dict->MetaIndexes( $this->name ) ) ) {
+                       foreach( $legacy_indexes as $index => $index_details ) {
+                               $sql[] = $xmls->dict->DropIndexSQL( $index, $this->name );
+                       }
+               }
+               
+               // remove fields to be dropped from table object
+               foreach( $this->drop_field as $field ) {
+                       unset( $this->fields[$field] );
+               }
+               
+               // if table exists
+               if( is_array( $legacy_fields = $xmls->dict->MetaColumns( $this->name ) ) ) {
+                       // drop table
+                       if( $this->drop_table ) {
+                               $sql[] = $xmls->dict->DropTableSQL( $this->name );
+                               
+                               return $sql;
+                       }
+                       
+                       // drop any existing fields not in schema
+                       foreach( $legacy_fields as $field_id => $field ) {
+                               if( !isset( $this->fields[$field_id] ) ) {
+                                       $sql[] = $xmls->dict->DropColumnSQL( $this->name, '`'.$field->name.'`' );
+                               }
+                       }
+               // if table doesn't exist
+               } else {
+                       if( $this->drop_table ) {
+                               return $sql;
+                       }
+                       
+                       $legacy_fields = array();
+               }
+               
+               // Loop through the field specifier array, building the associative array for the field options
+               $fldarray = array();
+               
+               foreach( $this->fields as $field_id => $finfo ) {
+                       // Set an empty size if it isn't supplied
+                       if( !isset( $finfo['SIZE'] ) ) {
+                               $finfo['SIZE'] = '';
+                       }
+                       
+                       // Initialize the field array with the type and size
+                       $fldarray[$field_id] = array(
+                               'NAME' => $finfo['NAME'],
+                               'TYPE' => $finfo['TYPE'],
+                               'SIZE' => $finfo['SIZE']
+                       );
+                       
+                       // Loop through the options array and add the field options. 
+                       if( isset( $finfo['OPTS'] ) ) {
+                               foreach( $finfo['OPTS'] as $opt ) {
+                                       // Option has an argument.
+                                       if( is_array( $opt ) ) {
+                                               $key = key( $opt );
+                                               $value = $opt[key( $opt )];
+                                               @$fldarray[$field_id][$key] .= $value;
+                                       // Option doesn't have arguments
+                                       } else {
+                                               $fldarray[$field_id][$opt] = $opt;
+                                       }
+                               }
+                       }
+               }
+               
+               if( empty( $legacy_fields ) ) {
+                       // Create the new table
+                       $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
+                       logMsg( end( $sql ), 'Generated CreateTableSQL' );
+               } else {
+                       // Upgrade an existing table
+                       logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" );
+                       switch( $xmls->upgrade ) {
+                               // Use ChangeTableSQL
+                               case 'ALTER':
+                                       logMsg( 'Generated ChangeTableSQL (ALTERing table)' );
+                                       $sql[] = $xmls->dict->ChangeTableSQL( $this->name, $fldarray, $this->opts );
+                                       break;
+                               case 'REPLACE':
+                                       logMsg( 'Doing upgrade REPLACE (testing)' );
+                                       $sql[] = $xmls->dict->DropTableSQL( $this->name );
+                                       $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
+                                       break;
+                               // ignore table
+                               default:
+                                       return array();
+                       }
+               }
+               
+               foreach( $this->indexes as $index ) {
+                       $sql[] = $index->create( $xmls );
+               }
+               
+               if( isset( $this->data ) ) {
+                       $sql[] = $this->data->create( $xmls );
+               }
+               
+               return $sql;
+       }
+       
+       /**
+       * Marks a field or table for destruction
+       */
+       function drop() {
+               if( isset( $this->current_field ) ) {
+                       // Drop the current field
+                       logMsg( "Dropping field '{$this->current_field}' from table '{$this->name}'" );
+                       // $this->drop_field[$this->current_field] = $xmls->dict->DropColumnSQL( $this->name, $this->current_field );
+                       $this->drop_field[$this->current_field] = $this->current_field;
+               } else {
+                       // Drop the current table
+                       logMsg( "Dropping table '{$this->name}'" );
+                       // $this->drop_table = $xmls->dict->DropTableSQL( $this->name );
+                       $this->drop_table = TRUE;
+               }
+       }
+}
+
+/**
+* Creates an index object in ADOdb's datadict format
+*
+* This class stores information about a database index. As charactaristics
+* of the index are loaded from the external source, methods and properties
+* of this class are used to build up the index description in ADOdb's
+* datadict format.
+*
+* @package axmls
+* @access private
+*/
+class dbIndex extends dbObject {
+       
+       /**
+       * @var string   Index name
+       */
+       var $name;
+       
+       /**
+       * @var array    Index options: Index-level options
+       */
+       var $opts = array();
+       
+       /**
+       * @var array    Indexed fields: Table columns included in this index
+       */
+       var $columns = array();
+       
+       /**
+       * @var boolean Mark index for destruction
+       * @access private
+       */
+       var $drop = FALSE;
+       
+       /**
+       * Initializes the new dbIndex object.
+       *
+       * @param object $parent Parent object
+       * @param array $attributes Attributes
+       *
+       * @internal
+       */
+       function dbIndex( &$parent, $attributes = NULL ) {
+               $this->parent = $parent;
+               
+               $this->name = $this->prefix ($attributes['NAME']);
+       }
+       
+       /**
+       * XML Callback to process start elements
+       *
+       * Processes XML opening tags. 
+       * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH. 
+       *
+       * @access private
+       */
+       function _tag_open( &$parser, $tag, $attributes ) {
+               $this->currentElement = strtoupper( $tag );
+               
+               switch( $this->currentElement ) {
+                       case 'DROP':
+                               $this->drop();
+                               break;
+                       case 'CLUSTERED':
+                       case 'BITMAP':
+                       case 'UNIQUE':
+                       case 'FULLTEXT':
+                       case 'HASH':
+                               // Add index Option
+                               $this->addIndexOpt( $this->currentElement );
+                               break;
+                       default:
+                               // print_r( array( $tag, $attributes ) );
+               }
+       }
+       
+       /**
+       * XML Callback to process CDATA elements
+       *
+       * Processes XML cdata.
+       *
+       * @access private
+       */
+       function _tag_cdata( &$parser, $cdata ) {
+               switch( $this->currentElement ) {
+                       // Index field name
+                       case 'COL':
+                               $this->addField( $cdata );
+                               break;
+                       default:
+                               
+               }
+       }
+       
+       /**
+       * XML Callback to process end elements
+       *
+       * @access private
+       */
+       function _tag_close( &$parser, $tag ) {
+               $this->currentElement = '';
+               
+               switch( strtoupper( $tag ) ) {
+                       case 'INDEX':
+                               xml_set_object( $parser, $this->parent );
+                               break;
+               }
+       }
+       
+       /**
+       * Adds a field to the index
+       *
+       * @param string $name Field name
+       * @return string Field list
+       */
+       function addField( $name ) {
+               $this->columns[$this->FieldID( $name )] = $name;
+               
+               // Return the field list
+               return $this->columns;
+       }
+       
+       /**
+       * Adds options to the index
+       *
+       * @param string $opt Comma-separated list of index options.
+       * @return string Option list
+       */
+       function addIndexOpt( $opt ) {
+               $this->opts[] = $opt;
+               
+               // Return the options list
+               return $this->opts;
+       }
+       
+       /**
+       * Generates the SQL that will create the index in the database
+       *
+       * @param object $xmls adoSchema object
+       * @return array Array containing index creation SQL
+       */
+       function create( &$xmls ) {
+               if( $this->drop ) {
+                       return NULL;
+               }
+               
+               // eliminate any columns that aren't in the table
+               foreach( $this->columns as $id => $col ) {
+                       if( !isset( $this->parent->fields[$id] ) ) {
+                               unset( $this->columns[$id] );
+                       }
+               }
+               
+               return $xmls->dict->CreateIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts );
+       }
+       
+       /**
+       * Marks an index for destruction
+       */
+       function drop() {
+               $this->drop = TRUE;
+       }
+}
+
+/**
+* Creates a data object in ADOdb's datadict format
+*
+* This class stores information about table data.
+*
+* @package axmls
+* @access private
+*/
+class dbData extends dbObject {
+       
+       var $data = array();
+       
+       var $row;
+       
+       /**
+       * Initializes the new dbIndex object.
+       *
+       * @param object $parent Parent object
+       * @param array $attributes Attributes
+       *
+       * @internal
+       */
+       function dbData( &$parent, $attributes = NULL ) {
+               $this->parent = $parent;
+       }
+       
+       /**
+       * XML Callback to process start elements
+       *
+       * Processes XML opening tags. 
+       * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH. 
+       *
+       * @access private
+       */
+       function _tag_open( &$parser, $tag, $attributes ) {
+               $this->currentElement = strtoupper( $tag );
+               
+               switch( $this->currentElement ) {
+                       case 'ROW':
+                               $this->row = count( $this->data );
+                               $this->data[$this->row] = array();
+                               break;
+                       case 'F':
+                               $this->addField($attributes);
+                       default:
+                               // print_r( array( $tag, $attributes ) );
+               }
+       }
+       
+       /**
+       * XML Callback to process CDATA elements
+       *
+       * Processes XML cdata.
+       *
+       * @access private
+       */
+       function _tag_cdata( &$parser, $cdata ) {
+               switch( $this->currentElement ) {
+                       // Index field name
+                       case 'F':
+                               $this->addData( $cdata );
+                               break;
+                       default:
+                               
+               }
+       }
+       
+       /**
+       * XML Callback to process end elements
+       *
+       * @access private
+       */
+       function _tag_close( &$parser, $tag ) {
+               $this->currentElement = '';
+               
+               switch( strtoupper( $tag ) ) {
+                       case 'DATA':
+                               xml_set_object( $parser, $this->parent );
+                               break;
+               }
+       }
+       
+       /**
+       * Adds a field to the index
+       *
+       * @param string $name Field name
+       * @return string Field list
+       */
+       function addField( $attributes ) {
+               if( isset( $attributes['NAME'] ) ) {
+                       $name = $attributes['NAME'];
+               } else {
+                       $name = count($this->data[$this->row]);
+               }
+               
+               // Set the field index so we know where we are
+               $this->current_field = $this->FieldID( $name );
+       }
+       
+       /**
+       * Adds options to the index
+       *
+       * @param string $opt Comma-separated list of index options.
+       * @return string Option list
+       */
+       function addData( $cdata ) {
+               if( !isset( $this->data[$this->row] ) ) {
+                       $this->data[$this->row] = array();
+               }
+               
+               if( !isset( $this->data[$this->row][$this->current_field] ) ) {
+                       $this->data[$this->row][$this->current_field] = '';
+               }
+               
+               $this->data[$this->row][$this->current_field] .= $cdata;
+       }
+       
+       /**
+       * Generates the SQL that will create the index in the database
+       *
+       * @param object $xmls adoSchema object
+       * @return array Array containing index creation SQL
+       */
+       function create( &$xmls ) {
+               $table = $xmls->dict->TableName($this->parent->name);
+               $table_field_count = count($this->parent->fields);
+               $sql = array();
+               
+               // eliminate any columns that aren't in the table
+               foreach( $this->data as $row ) {
+                       $table_fields = $this->parent->fields;
+                       $fields = array();
+                       
+                       foreach( $row as $field_id => $field_data ) {
+                               if( !array_key_exists( $field_id, $table_fields ) ) {
+                                       if( is_numeric( $field_id ) ) {
+                                               $field_id = reset( array_keys( $table_fields ) );
+                                       } else {
+                                               continue;
+                                       }
+                               }
+                               
+                               $name = $table_fields[$field_id]['NAME'];
+                               
+                               switch( $table_fields[$field_id]['TYPE'] ) {
+                                       case 'C':
+                                       case 'C2':
+                                       case 'X':
+                                       case 'X2':
+                                               $fields[$name] = $xmls->db->qstr( $field_data );
+                                               break;
+                                       case 'I':
+                                       case 'I1':
+                                       case 'I2':
+                                       case 'I4':
+                                       case 'I8':
+                                               $fields[$name] = intval($field_data);
+                                               break;
+                                       default:
+                                               $fields[$name] = $field_data;
+                               }
+                               
+                               unset($table_fields[$field_id]);
+                       }
+                       
+                       // check that at least 1 column is specified
+                       if( empty( $fields ) ) {
+                               continue;
+                       }
+                       
+                       // check that no required columns are missing
+                       if( count( $fields ) < $table_field_count ) {
+                               foreach( $table_fields as $field ) {
+                                       if (isset( $field['OPTS'] ))
+                                               if( ( in_array( 'NOTNULL', $field['OPTS'] ) || in_array( 'KEY', $field['OPTS'] ) ) && !in_array( 'AUTOINCREMENT', $field['OPTS'] ) ) {
+                                                       continue(2);
+                                               }
+                               }
+                       }
+                       
+                       $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')';
+               }
+               
+               return $sql;
+       }
+}
+
+/**
+* Creates the SQL to execute a list of provided SQL queries
+*
+* @package axmls
+* @access private
+*/
+class dbQuerySet extends dbObject {
+       
+       /**
+       * @var array    List of SQL queries
+       */
+       var $queries = array();
+       
+       /**
+       * @var string   String used to build of a query line by line
+       */
+       var $query;
+       
+       /**
+       * @var string   Query prefix key
+       */
+       var $prefixKey = '';
+       
+       /**
+       * @var boolean  Auto prefix enable (TRUE)
+       */
+       var $prefixMethod = 'AUTO';
+       
+       /**
+       * Initializes the query set.
+       *
+       * @param object $parent Parent object
+       * @param array $attributes Attributes
+       */
+       function dbQuerySet( &$parent, $attributes = NULL ) {
+               $this->parent = $parent;
+                       
+               // Overrides the manual prefix key
+               if( isset( $attributes['KEY'] ) ) {
+                       $this->prefixKey = $attributes['KEY'];
+               }
+               
+               $prefixMethod = isset( $attributes['PREFIXMETHOD'] ) ? strtoupper( trim( $attributes['PREFIXMETHOD'] ) ) : '';
+               
+               // Enables or disables automatic prefix prepending
+               switch( $prefixMethod ) {
+                       case 'AUTO':
+                               $this->prefixMethod = 'AUTO';
+                               break;
+                       case 'MANUAL':
+                               $this->prefixMethod = 'MANUAL';
+                               break;
+                       case 'NONE':
+                               $this->prefixMethod = 'NONE';
+                               break;
+               }
+       }
+       
+       /**
+       * XML Callback to process start elements. Elements currently 
+       * processed are: QUERY. 
+       *
+       * @access private
+       */
+       function _tag_open( &$parser, $tag, $attributes ) {
+               $this->currentElement = strtoupper( $tag );
+               
+               switch( $this->currentElement ) {
+                       case 'QUERY':
+                               // Create a new query in a SQL queryset.
+                               // Ignore this query set if a platform is specified and it's different than the 
+                               // current connection platform.
+                               if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
+                                       $this->newQuery();
+                               } else {
+                                       $this->discardQuery();
+                               }
+                               break;
+                       default:
+                               // print_r( array( $tag, $attributes ) );
+               }
+       }
+       
+       /**
+       * XML Callback to process CDATA elements
+       */
+       function _tag_cdata( &$parser, $cdata ) {
+               switch( $this->currentElement ) {
+                       // Line of queryset SQL data
+                       case 'QUERY':
+                               $this->buildQuery( $cdata );
+                               break;
+                       default:
+                               
+               }
+       }
+       
+       /**
+       * XML Callback to process end elements
+       *
+       * @access private
+       */
+       function _tag_close( &$parser, $tag ) {
+               $this->currentElement = '';
+               
+               switch( strtoupper( $tag ) ) {
+                       case 'QUERY':
+                               // Add the finished query to the open query set.
+                               $this->addQuery();
+                               break;
+                       case 'SQL':
+                               $this->parent->addSQL( $this->create( $this->parent ) );
+                               xml_set_object( $parser, $this->parent );
+                               $this->destroy();
+                               break;
+                       default:
+                               
+               }
+       }
+       
+       /**
+       * Re-initializes the query.
+       *
+       * @return boolean TRUE
+       */
+       function newQuery() {
+               $this->query = '';
+               
+               return TRUE;
+       }
+       
+       /**
+       * Discards the existing query.
+       *
+       * @return boolean TRUE
+       */
+       function discardQuery() {
+               unset( $this->query );
+               
+               return TRUE;
+       }
+       
+       /** 
+       * Appends a line to a query that is being built line by line
+       *
+       * @param string $data Line of SQL data or NULL to initialize a new query
+       * @return string SQL query string.
+       */
+       function buildQuery( $sql = NULL ) {
+               if( !isset( $this->query ) OR empty( $sql ) ) {
+                       return FALSE;
+               }
+               
+               $this->query .= $sql;
+               
+               return $this->query;
+       }
+       
+       /**
+       * Adds a completed query to the query list
+       *
+       * @return string        SQL of added query
+       */
+       function addQuery() {
+               if( !isset( $this->query ) ) {
+                       return FALSE;
+               }
+               
+               $this->queries[] = $return = trim($this->query);
+               
+               unset( $this->query );
+               
+               return $return;
+       }
+       
+       /**
+       * Creates and returns the current query set
+       *
+       * @param object $xmls adoSchema object
+       * @return array Query set
+       */
+       function create( &$xmls ) {
+               foreach( $this->queries as $id => $query ) {
+                       switch( $this->prefixMethod ) {
+                               case 'AUTO':
+                                       // Enable auto prefix replacement
+                                       
+                                       // Process object prefix.
+                                       // Evaluate SQL statements to prepend prefix to objects
+                                       $query = $this->prefixQuery( '/^\s*((?is)INSERT\s+(INTO\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
+                                       $query = $this->prefixQuery( '/^\s*((?is)UPDATE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
+                                       $query = $this->prefixQuery( '/^\s*((?is)DELETE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
+                                       
+                                       // SELECT statements aren't working yet
+                                       #$data = preg_replace( '/(?ias)(^\s*SELECT\s+.*\s+FROM)\s+(\W\s*,?\s*)+((?i)\s+WHERE.*$)/', "\1 $prefix\2 \3", $data );
+                                       
+                               case 'MANUAL':
+                                       // If prefixKey is set and has a value then we use it to override the default constant XMLS_PREFIX.
+                                       // If prefixKey is not set, we use the default constant XMLS_PREFIX
+                                       if( isset( $this->prefixKey ) AND( $this->prefixKey !== '' ) ) {
+                                               // Enable prefix override
+                                               $query = str_replace( $this->prefixKey, $xmls->objectPrefix, $query );
+                                       } else {
+                                               // Use default replacement
+                                               $query = str_replace( XMLS_PREFIX , $xmls->objectPrefix, $query );
+                                       }
+                       }
+                       
+                       $this->queries[$id] = trim( $query );
+               }
+               
+               // Return the query set array
+               return $this->queries;
+       }
+       
+       /**
+       * Rebuilds the query with the prefix attached to any objects
+       *
+       * @param string $regex Regex used to add prefix
+       * @param string $query SQL query string
+       * @param string $prefix Prefix to be appended to tables, indices, etc.
+       * @return string Prefixed SQL query string.
+       */
+       function prefixQuery( $regex, $query, $prefix = NULL ) {
+               if( !isset( $prefix ) ) {
+                       return $query;
+               }
+               
+               if( preg_match( $regex, $query, $match ) ) {
+                       $preamble = $match[1];
+                       $postamble = $match[5];
+                       $objectList = explode( ',', $match[3] );
+                       // $prefix = $prefix . '_';
+                       
+                       $prefixedList = '';
+                       
+                       foreach( $objectList as $object ) {
+                               if( $prefixedList !== '' ) {
+                                       $prefixedList .= ', ';
+                               }
+                               
+                               $prefixedList .= $prefix . trim( $object );
+                       }
+                       
+                       $query = $preamble . ' ' . $prefixedList . ' ' . $postamble;
+               }
+               
+               return $query;
+       }
+}
+
+/**
+* Loads and parses an XML file, creating an array of "ready-to-run" SQL statements
+* 
+* This class is used to load and parse the XML file, to create an array of SQL statements
+* that can be used to build a database, and to build the database using the SQL array.
+*
+* @tutorial getting_started.pkg
+*
+* @author Richard Tango-Lowy & Dan Cech
+* @version $Revision$
+*
+* @package axmls
+*/
+class adoSchema {
+       
+       /**
+       * @var array    Array containing SQL queries to generate all objects
+       * @access private
+       */
+       var $sqlArray;
+       
+       /**
+       * @var object   ADOdb connection object
+       * @access private
+       */
+       var $db;
+       
+       /**
+       * @var object   ADOdb Data Dictionary
+       * @access private
+       */
+       var $dict;
+       
+       /**
+       * @var string Current XML element
+       * @access private
+       */
+       var $currentElement = '';
+       
+       /**
+       * @var string If set (to 'ALTER' or 'REPLACE'), upgrade an existing database
+       * @access private
+       */
+       var $upgrade = '';
+       
+       /**
+       * @var string Optional object prefix
+       * @access private
+       */
+       var $objectPrefix = '';
+       
+       /**
+       * @var long     Original Magic Quotes Runtime value
+       * @access private
+       */
+       var $mgq;
+       
+       /**
+       * @var long     System debug
+       * @access private
+       */
+       var $debug;
+       
+       /**
+       * @var string Regular expression to find schema version
+       * @access private
+       */
+       var $versionRegex = '/<schema.*?( version="([^"]*)")?.*?>/';
+       
+       /**
+       * @var string Current schema version
+       * @access private
+       */
+       var $schemaVersion;
+       
+       /**
+       * @var int      Success of last Schema execution
+       */
+       var $success;
+       
+       /**
+       * @var bool     Execute SQL inline as it is generated
+       */
+       var $executeInline;
+       
+       /**
+       * @var bool     Continue SQL execution if errors occur
+       */
+       var $continueOnError;
+       
+       /**
+       * Creates an adoSchema object
+       *
+       * Creating an adoSchema object is the first step in processing an XML schema.
+       * The only parameter is an ADOdb database connection object, which must already
+       * have been created.
+       *
+       * @param object $db ADOdb database connection object.
+       */
+       function adoSchema( &$db ) {
+               // Initialize the environment
+               $this->mgq = get_magic_quotes_runtime();
+               set_magic_quotes_runtime(0);
+               
+               $this->db = $db;
+               $this->debug = $this->db->debug;
+               $this->dict = NewDataDictionary( $this->db );
+               $this->sqlArray = array();
+               $this->schemaVersion = XMLS_SCHEMA_VERSION;
+               $this->executeInline( XMLS_EXECUTE_INLINE );
+               $this->continueOnError( XMLS_CONTINUE_ON_ERROR );
+               $this->setUpgradeMethod();
+       }
+       
+       /**
+       * Sets the method to be used for upgrading an existing database
+       *
+       * Use this method to specify how existing database objects should be upgraded.
+       * The method option can be set to ALTER, REPLACE, BEST, or NONE. ALTER attempts to
+       * alter each database object directly, REPLACE attempts to rebuild each object
+       * from scratch, BEST attempts to determine the best upgrade method for each
+       * object, and NONE disables upgrading.
+       *
+       * This method is not yet used by AXMLS, but exists for backward compatibility.
+       * The ALTER method is automatically assumed when the adoSchema object is
+       * instantiated; other upgrade methods are not currently supported.
+       *
+       * @param string $method Upgrade method (ALTER|REPLACE|BEST|NONE)
+       * @returns string Upgrade method used
+       */
+       function SetUpgradeMethod( $method = '' ) {
+               if( !is_string( $method ) ) {
+                       return FALSE;
+               }
+               
+               $method = strtoupper( $method );
+               
+               // Handle the upgrade methods
+               switch( $method ) {
+                       case 'ALTER':
+                               $this->upgrade = $method;
+                               break;
+                       case 'REPLACE':
+                               $this->upgrade = $method;
+                               break;
+                       case 'BEST':
+                               $this->upgrade = 'ALTER';
+                               break;
+                       case 'NONE':
+                               $this->upgrade = 'NONE';
+                               break;
+                       default:
+                               // Use default if no legitimate method is passed.
+                               $this->upgrade = XMLS_DEFAULT_UPGRADE_METHOD;
+               }
+               
+               return $this->upgrade;
+       }
+       
+       /**
+       * Enables/disables inline SQL execution.
+       *
+       * Call this method to enable or disable inline execution of the schema. If the mode is set to TRUE (inline execution),
+       * AXMLS applies the SQL to the database immediately as each schema entity is parsed. If the mode
+       * is set to FALSE (post execution), AXMLS parses the entire schema and you will need to call adoSchema::ExecuteSchema()
+       * to apply the schema to the database.
+       *
+       * @param bool $mode execute
+       * @return bool current execution mode
+       *
+       * @see ParseSchema(), ExecuteSchema()
+       */
+       function ExecuteInline( $mode = NULL ) {
+               if( is_bool( $mode ) ) {
+                       $this->executeInline = $mode;
+               }
+               
+               return $this->executeInline;
+       }
+       
+       /**
+       * Enables/disables SQL continue on error.
+       *
+       * Call this method to enable or disable continuation of SQL execution if an error occurs.
+       * If the mode is set to TRUE (continue), AXMLS will continue to apply SQL to the database, even if an error occurs.
+       * If the mode is set to FALSE (halt), AXMLS will halt execution of generated sql if an error occurs, though parsing
+       * of the schema will continue.
+       *
+       * @param bool $mode execute
+       * @return bool current continueOnError mode
+       *
+       * @see addSQL(), ExecuteSchema()
+       */
+       function ContinueOnError( $mode = NULL ) {
+               if( is_bool( $mode ) ) {
+                       $this->continueOnError = $mode;
+               }
+               
+               return $this->continueOnError;
+       }
+       
+       /**
+       * Loads an XML schema from a file and converts it to SQL.
+       *
+       * Call this method to load the specified schema (see the DTD for the proper format) from
+       * the filesystem and generate the SQL necessary to create the database described. 
+       * @see ParseSchemaString()
+       *
+       * @param string $file Name of XML schema file.
+       * @param bool $returnSchema Return schema rather than parsing.
+       * @return array Array of SQL queries, ready to execute
+       */
+       function ParseSchema( $filename, $returnSchema = FALSE ) {
+               return $this->ParseSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );
+       }
+       
+       /**
+       * Loads an XML schema from a file and converts it to SQL.
+       *
+       * Call this method to load the specified schema from a file (see the DTD for the proper format) 
+       * and generate the SQL necessary to create the database described by the schema.
+       *
+       * @param string $file Name of XML schema file.
+       * @param bool $returnSchema Return schema rather than parsing.
+       * @return array Array of SQL queries, ready to execute.
+       *
+       * @deprecated Replaced by adoSchema::ParseSchema() and adoSchema::ParseSchemaString()
+       * @see ParseSchema(), ParseSchemaString()
+       */
+       function ParseSchemaFile( $filename, $returnSchema = FALSE ) {
+               // Open the file
+               if( !($fp = fopen( $filename, 'r' )) ) {
+                       // die( 'Unable to open file' );
+                       return FALSE;
+               }
+               
+               // do version detection here
+               if( $this->SchemaFileVersion( $filename ) != $this->schemaVersion ) {
+                       return FALSE;
+               }
+               
+               if ( $returnSchema )
+               {
+                       $xmlstring = '';
+                       while( $data = fread( $fp, 100000 ) ) {
+                               $xmlstring .= $data;
+                       }
+                       return $xmlstring;
+               }
+               
+               $this->success = 2;
+               
+               $xmlParser = $this->create_parser();
+               
+               // Process the file
+               while( $data = fread( $fp, 4096 ) ) {
+                       if( !xml_parse( $xmlParser, $data, feof( $fp ) ) ) {
+                               die( sprintf(
+                                       "XML error: %s at line %d",
+                                       xml_error_string( xml_get_error_code( $xmlParser) ),
+                                       xml_get_current_line_number( $xmlParser)
+                               ) );
+                       }
+               }
+               
+               xml_parser_free( $xmlParser );
+               
+               return $this->sqlArray;
+       }
+       
+       /**
+       * Converts an XML schema string to SQL.
+       *
+       * Call this method to parse a string containing an XML schema (see the DTD for the proper format)
+       * and generate the SQL necessary to create the database described by the schema. 
+       * @see ParseSchema()
+       *
+       * @param string $xmlstring XML schema string.
+       * @param bool $returnSchema Return schema rather than parsing.
+       * @return array Array of SQL queries, ready to execute.
+       */
+       function ParseSchemaString( $xmlstring, $returnSchema = FALSE ) {
+               if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {
+                       return FALSE;
+               }
+               
+               // do version detection here
+               if( $this->SchemaStringVersion( $xmlstring ) != $this->schemaVersion ) {
+                       return FALSE;
+               }
+               
+               if ( $returnSchema )
+               {
+                       return $xmlstring;
+               }
+               
+               $this->success = 2;
+               
+               $xmlParser = $this->create_parser();
+               
+               if( !xml_parse( $xmlParser, $xmlstring, TRUE ) ) {
+                       die( sprintf(
+                               "XML error: %s at line %d",
+                               xml_error_string( xml_get_error_code( $xmlParser) ),
+                               xml_get_current_line_number( $xmlParser)
+                       ) );
+               }
+               
+               xml_parser_free( $xmlParser );
+               
+               return $this->sqlArray;
+       }
+       
+       /**
+       * Loads an XML schema from a file and converts it to uninstallation SQL.
+       *
+       * Call this method to load the specified schema (see the DTD for the proper format) from
+       * the filesystem and generate the SQL necessary to remove the database described.
+       * @see RemoveSchemaString()
+       *
+       * @param string $file Name of XML schema file.
+       * @param bool $returnSchema Return schema rather than parsing.
+       * @return array Array of SQL queries, ready to execute
+       */
+       function RemoveSchema( $filename, $returnSchema = FALSE ) {
+               return $this->RemoveSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );
+       }
+       
+       /**
+       * Converts an XML schema string to uninstallation SQL.
+       *
+       * Call this method to parse a string containing an XML schema (see the DTD for the proper format)
+       * and generate the SQL necessary to uninstall the database described by the schema. 
+       * @see RemoveSchema()
+       *
+       * @param string $schema XML schema string.
+       * @param bool $returnSchema Return schema rather than parsing.
+       * @return array Array of SQL queries, ready to execute.
+       */
+       function RemoveSchemaString( $schema, $returnSchema = FALSE ) {
+               
+               // grab current version
+               if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {
+                       return FALSE;
+               }
+               
+               return $this->ParseSchemaString( $this->TransformSchema( $schema, 'remove-' . $version), $returnSchema );
+       }
+       
+       /**
+       * Applies the current XML schema to the database (post execution).
+       *
+       * Call this method to apply the current schema (generally created by calling 
+       * ParseSchema() or ParseSchemaString() ) to the database (creating the tables, indexes, 
+       * and executing other SQL specified in the schema) after parsing.
+       * @see ParseSchema(), ParseSchemaString(), ExecuteInline()
+       *
+       * @param array $sqlArray Array of SQL statements that will be applied rather than
+       *               the current schema.
+       * @param boolean $continueOnErr Continue to apply the schema even if an error occurs.
+       * @returns integer 0 if failure, 1 if errors, 2 if successful.
+       */
+       function ExecuteSchema( $sqlArray = NULL, $continueOnErr =  NULL ) {
+               if( !is_bool( $continueOnErr ) ) {
+                       $continueOnErr = $this->ContinueOnError();
+               }
+               
+               if( !isset( $sqlArray ) ) {
+                       $sqlArray = $this->sqlArray;
+               }
+               
+               if( !is_array( $sqlArray ) ) {
+                       $this->success = 0;
+               } else {
+                       $this->success = $this->dict->ExecuteSQLArray( $sqlArray, $continueOnErr );
+               }
+               
+               return $this->success;
+       }
+       
+       /**
+       * Returns the current SQL array. 
+       *
+       * Call this method to fetch the array of SQL queries resulting from 
+       * ParseSchema() or ParseSchemaString(). 
+       *
+       * @param string $format Format: HTML, TEXT, or NONE (PHP array)
+       * @return array Array of SQL statements or FALSE if an error occurs
+       */
+       function PrintSQL( $format = 'NONE' ) {
+               $sqlArray = null;
+               return $this->getSQL( $format, $sqlArray );
+       }
+       
+       /**
+       * Saves the current SQL array to the local filesystem as a list of SQL queries.
+       *
+       * Call this method to save the array of SQL queries (generally resulting from a
+       * parsed XML schema) to the filesystem.
+       *
+       * @param string $filename Path and name where the file should be saved.
+       * @return boolean TRUE if save is successful, else FALSE. 
+       */
+       function SaveSQL( $filename = './schema.sql' ) {
+               
+               if( !isset( $sqlArray ) ) {
+                       $sqlArray = $this->sqlArray;
+               }
+               if( !isset( $sqlArray ) ) {
+                       return FALSE;
+               }
+               
+               $fp = fopen( $filename, "w" );
+               
+               foreach( $sqlArray as $key => $query ) {
+                       fwrite( $fp, $query . ";\n" );
+               }
+               fclose( $fp );
+       }
+       
+       /**
+       * Create an xml parser
+       *
+       * @return object PHP XML parser object
+       *
+       * @access private
+       */
+       function create_parser() {
+               // Create the parser
+               $xmlParser = xml_parser_create();
+               xml_set_object( $xmlParser, $this );
+               
+               // Initialize the XML callback functions
+               xml_set_element_handler( $xmlParser, '_tag_open', '_tag_close' );
+               xml_set_character_data_handler( $xmlParser, '_tag_cdata' );
+               
+               return $xmlParser;
+       }
+       
+       /**
+       * XML Callback to process start elements
+       *
+       * @access private
+       */
+       function _tag_open( &$parser, $tag, $attributes ) {
+               switch( strtoupper( $tag ) ) {
+                       case 'TABLE':
+                               $this->obj = new dbTable( $this, $attributes );
+                               xml_set_object( $parser, $this->obj );
+                               break;
+                       case 'SQL':
+                               if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
+                                       $this->obj = new dbQuerySet( $this, $attributes );
+                                       xml_set_object( $parser, $this->obj );
+                               }
+                               break;
+                       default:
+                               // print_r( array( $tag, $attributes ) );
+               }
+               
+       }
+       
+       /**
+       * XML Callback to process CDATA elements
+       *
+       * @access private
+       */
+       function _tag_cdata( &$parser, $cdata ) {
+       }
+       
+       /**
+       * XML Callback to process end elements
+       *
+       * @access private
+       * @internal
+       */
+       function _tag_close( &$parser, $tag ) {
+               
+       }
+       
+       /**
+       * Converts an XML schema string to the specified DTD version.
+       *
+       * Call this method to convert a string containing an XML schema to a different AXMLS
+       * DTD version. For instance, to convert a schema created for an pre-1.0 version for 
+       * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version 
+       * parameter is specified, the schema will be converted to the current DTD version. 
+       * If the newFile parameter is provided, the converted schema will be written to the specified
+       * file.
+       * @see ConvertSchemaFile()
+       *
+       * @param string $schema String containing XML schema that will be converted.
+       * @param string $newVersion DTD version to convert to.
+       * @param string $newFile File name of (converted) output file.
+       * @return string Converted XML schema or FALSE if an error occurs.
+       */
+       function ConvertSchemaString( $schema, $newVersion = NULL, $newFile = NULL ) {
+               
+               // grab current version
+               if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {
+                       return FALSE;
+               }
+               
+               if( !isset ($newVersion) ) {
+                       $newVersion = $this->schemaVersion;
+               }
+               
+               if( $version == $newVersion ) {
+                       $result = $schema;
+               } else {
+                       $result = $this->TransformSchema( $schema, 'convert-' . $version . '-' . $newVersion);
+               }
+               
+               if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {
+                       fwrite( $fp, $result );
+                       fclose( $fp );
+               }
+               
+               return $result;
+       }
+       
+       // compat for pre-4.3 - jlim
+       function _file_get_contents($path)
+       {
+               if (function_exists('file_get_contents')) return file_get_contents($path);
+               return join('',file($path));
+       }
+       
+       /**
+       * Converts an XML schema file to the specified DTD version.
+       *
+       * Call this method to convert the specified XML schema file to a different AXMLS
+       * DTD version. For instance, to convert a schema created for an pre-1.0 version for 
+       * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version 
+       * parameter is specified, the schema will be converted to the current DTD version. 
+       * If the newFile parameter is provided, the converted schema will be written to the specified
+       * file.
+       * @see ConvertSchemaString()
+       *
+       * @param string $filename Name of XML schema file that will be converted.
+       * @param string $newVersion DTD version to convert to.
+       * @param string $newFile File name of (converted) output file.
+       * @return string Converted XML schema or FALSE if an error occurs.
+       */
+       function ConvertSchemaFile( $filename, $newVersion = NULL, $newFile = NULL ) {
+               
+               // grab current version
+               if( !( $version = $this->SchemaFileVersion( $filename ) ) ) {
+                       return FALSE;
+               }
+               
+               if( !isset ($newVersion) ) {
+                       $newVersion = $this->schemaVersion;
+               }
+               
+               if( $version == $newVersion ) {
+                       $result = _file_get_contents( $filename );
+                       
+                       // remove unicode BOM if present
+                       if( substr( $result, 0, 3 ) == sprintf( '%c%c%c', 239, 187, 191 ) ) {
+                               $result = substr( $result, 3 );
+                       }
+               } else {
+                       $result = $this->TransformSchema( $filename, 'convert-' . $version . '-' . $newVersion, 'file' );
+               }
+               
+               if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {
+                       fwrite( $fp, $result );
+                       fclose( $fp );
+               }
+               
+               return $result;
+       }
+       
+       function TransformSchema( $schema, $xsl, $schematype='string' )
+       {
+               // Fail if XSLT extension is not available
+               if( ! function_exists( 'xslt_create' ) ) {
+                       return FALSE;
+               }
+               
+               $xsl_file = dirname( __FILE__ ) . '/xsl/' . $xsl . '.xsl';
+               
+               // look for xsl
+               if( !is_readable( $xsl_file ) ) {
+                       return FALSE;
+               }
+               
+               switch( $schematype )
+               {
+                       case 'file':
+                               if( !is_readable( $schema ) ) {
+                                       return FALSE;
+                               }
+                               
+                               $schema = _file_get_contents( $schema );
+                               break;
+                       case 'string':
+                       default:
+                               if( !is_string( $schema ) ) {
+                                       return FALSE;
+                               }
+               }
+               
+               $arguments = array (
+                       '/_xml' => $schema,
+                       '/_xsl' => _file_get_contents( $xsl_file )
+               );
+               
+               // create an XSLT processor
+               $xh = xslt_create ();
+               
+               // set error handler
+               xslt_set_error_handler ($xh, array (&$this, 'xslt_error_handler'));
+               
+               // process the schema
+               $result = xslt_process ($xh, 'arg:/_xml', 'arg:/_xsl', NULL, $arguments); 
+               
+               xslt_free ($xh);
+               
+               return $result;
+       }
+       
+       /**
+       * Processes XSLT transformation errors
+       *
+       * @param object $parser XML parser object
+       * @param integer $errno Error number
+       * @param integer $level Error level
+       * @param array $fields Error information fields
+       *
+       * @access private
+       */
+       function xslt_error_handler( $parser, $errno, $level, $fields ) {
+               if( is_array( $fields ) ) {
+                       $msg = array(
+                               'Message Type' => ucfirst( $fields['msgtype'] ),
+                               'Message Code' => $fields['code'],
+                               'Message' => $fields['msg'],
+                               'Error Number' => $errno,
+                               'Level' => $level
+                       );
+                       
+                       switch( $fields['URI'] ) {
+                               case 'arg:/_xml':
+                                       $msg['Input'] = 'XML';
+                                       break;
+                               case 'arg:/_xsl':
+                                       $msg['Input'] = 'XSL';
+                                       break;
+                               default:
+                                       $msg['Input'] = $fields['URI'];
+                       }
+                       
+                       $msg['Line'] = $fields['line'];
+               } else {
+                       $msg = array(
+                               'Message Type' => 'Error',
+                               'Error Number' => $errno,
+                               'Level' => $level,
+                               'Fields' => var_export( $fields, TRUE )
+                       );
+               }
+               
+               $error_details = $msg['Message Type'] . ' in XSLT Transformation' . "\n"
+                                          . '<table>' . "\n";
+               
+               foreach( $msg as $label => $details ) {
+                       $error_details .= '<tr><td><b>' . $label . ': </b></td><td>' . htmlentities( $details ) . '</td></tr>' . "\n";
+               }
+               
+               $error_details .= '</table>';
+               
+               trigger_error( $error_details, E_USER_ERROR );
+       }
+       
+       /**
+       * Returns the AXMLS Schema Version of the requested XML schema file.
+       *
+       * Call this method to obtain the AXMLS DTD version of the requested XML schema file.
+       * @see SchemaStringVersion()
+       *
+       * @param string $filename AXMLS schema file
+       * @return string Schema version number or FALSE on error
+       */
+       function SchemaFileVersion( $filename ) {
+               // Open the file
+               if( !($fp = fopen( $filename, 'r' )) ) {
+                       // die( 'Unable to open file' );
+                       return FALSE;
+               }
+               
+               // Process the file
+               while( $data = fread( $fp, 4096 ) ) {
+                       if( preg_match( $this->versionRegex, $data, $matches ) ) {
+                               return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;
+                       }
+               }
+               
+               return FALSE;
+       }
+       
+       /**
+       * Returns the AXMLS Schema Version of the provided XML schema string.
+       *
+       * Call this method to obtain the AXMLS DTD version of the provided XML schema string.
+       * @see SchemaFileVersion()
+       *
+       * @param string $xmlstring XML schema string
+       * @return string Schema version number or FALSE on error
+       */
+       function SchemaStringVersion( $xmlstring ) {
+               if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {
+                       return FALSE;
+               }
+               
+               if( preg_match( $this->versionRegex, $xmlstring, $matches ) ) {
+                       return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;
+               }
+               
+               return FALSE;
+       }
+       
+       /**
+       * Extracts an XML schema from an existing database.
+       *
+       * Call this method to create an XML schema string from an existing database.
+       * If the data parameter is set to TRUE, AXMLS will include the data from the database
+       * in the schema. 
+       *
+       * @param boolean $data Include data in schema dump
+       * @return string Generated XML schema
+       */
+       function ExtractSchema( $data = FALSE ) {
+               $old_mode = $this->db->SetFetchMode( ADODB_FETCH_NUM );
+               
+               $schema = '<?xml version="1.0"?>' . "\n"
+                               . '<schema version="' . $this->schemaVersion . '">' . "\n";
+               
+               if( is_array( $tables = $this->db->MetaTables( 'TABLES' ) ) ) {
+                       foreach( $tables as $table ) {
+                               $schema .= '    <table name="' . $table . '">' . "\n";
+                               
+                               // grab details from database
+                               $rs = $this->db->Execute( 'SELECT * FROM ' . $table . ' WHERE 1=1' );
+                               $fields = $this->db->MetaColumns( $table );
+                               $indexes = $this->db->MetaIndexes( $table );
+                               
+                               if( is_array( $fields ) ) {
+                                       foreach( $fields as $details ) {
+                                               $extra = '';
+                                               $content = array();
+                                               
+                                               if( $details->max_length > 0 ) {
+                                                       $extra .= ' size="' . $details->max_length . '"';
+                                               }
+                                               
+                                               if( $details->primary_key ) {
+                                                       $content[] = '<KEY/>';
+                                               } elseif( $details->not_null ) {
+                                                       $content[] = '<NOTNULL/>';
+                                               }
+                                               
+                                               if( $details->has_default ) {
+                                                       $content[] = '<DEFAULT value="' . $details->default_value . '"/>';
+                                               }
+                                               
+                                               if( $details->auto_increment ) {
+                                                       $content[] = '<AUTOINCREMENT/>';
+                                               }
+                                               
+                                               // this stops the creation of 'R' columns,
+                                               // AUTOINCREMENT is used to create auto columns
+                                               $details->primary_key = 0;
+                                               $type = $rs->MetaType( $details );
+                                               
+                                               $schema .= '            <field name="' . $details->name . '" type="' . $type . '"' . $extra . '>';
+                                               
+                                               if( !empty( $content ) ) {
+                                                       $schema .= "\n                  " . implode( "\n                        ", $content ) . "\n             ";
+                                               }
+                                               
+                                               $schema .= '</field>' . "\n";
+                                       }
+                               }
+                               
+                               if( is_array( $indexes ) ) {
+                                       foreach( $indexes as $index => $details ) {
+                                               $schema .= '            <index name="' . $index . '">' . "\n";
+                                               
+                                               if( $details['unique'] ) {
+                                                       $schema .= '                    <UNIQUE/>' . "\n";
+                                               }
+                                               
+                                               foreach( $details['columns'] as $column ) {
+                                                       $schema .= '                    <col>' . $column . '</col>' . "\n";
+                                               }
+                                               
+                                               $schema .= '            </index>' . "\n";
+                                       }
+                               }
+                               
+                               if( $data ) {
+                                       $rs = $this->db->Execute( 'SELECT * FROM ' . $table );
+                                       
+                                       if( is_object( $rs ) ) {
+                                               $schema .= '            <data>' . "\n";
+                                               
+                                               while( $row = $rs->FetchRow() ) {
+                                                       foreach( $row as $key => $val ) {
+                                                               $row[$key] = htmlentities($val);
+                                                       }
+                                                       
+                                                       $schema .= '                    <row><f>' . implode( '</f><f>', $row ) . '</f></row>' . "\n";
+                                               }
+                                               
+                                               $schema .= '            </data>' . "\n";
+                                       }
+                               }
+                               
+                               $schema .= '    </table>' . "\n";
+                       }
+               }
+               
+               $this->db->SetFetchMode( $old_mode );
+               
+               $schema .= '</schema>';
+               return $schema;
+       }
+       
+       /**
+       * Sets a prefix for database objects
+       *
+       * Call this method to set a standard prefix that will be prepended to all database tables 
+       * and indices when the schema is parsed. Calling setPrefix with no arguments clears the prefix.
+       *
+       * @param string $prefix Prefix that will be prepended.
+       * @param boolean $underscore If TRUE, automatically append an underscore character to the prefix.
+       * @return boolean TRUE if successful, else FALSE
+       */
+       function SetPrefix( $prefix = '', $underscore = TRUE ) {
+               switch( TRUE ) {
+                       // clear prefix
+                       case empty( $prefix ):
+                               logMsg( 'Cleared prefix' );
+                               $this->objectPrefix = '';
+                               return TRUE;
+                       // prefix too long
+                       case strlen( $prefix ) > XMLS_PREFIX_MAXLEN:
+                       // prefix contains invalid characters
+                       case !preg_match( '/^[a-z][a-z0-9_]+$/i', $prefix ):
+                               logMsg( 'Invalid prefix: ' . $prefix );
+                               return FALSE;
+               }
+               
+               if( $underscore AND substr( $prefix, -1 ) != '_' ) {
+                       $prefix .= '_';
+               }
+               
+               // prefix valid
+               logMsg( 'Set prefix: ' . $prefix );
+               $this->objectPrefix = $prefix;
+               return TRUE;
+       }
+       
+       /**
+       * Returns an object name with the current prefix prepended.
+       *
+       * @param string $name Name
+       * @return string        Prefixed name
+       *
+       * @access private
+       */
+       function prefix( $name = '' ) {
+               // if prefix is set
+               if( !empty( $this->objectPrefix ) ) {
+                       // Prepend the object prefix to the table name
+                       // prepend after quote if used
+                       return preg_replace( '/^(`?)(.+)$/', '$1' . $this->objectPrefix . '$2', $name );
+               }
+               
+               // No prefix set. Use name provided.
+               return $name;
+       }
+       
+       /**
+       * Checks if element references a specific platform
+       *
+       * @param string $platform Requested platform
+       * @returns boolean TRUE if platform check succeeds
+       *
+       * @access private
+       */
+       function supportedPlatform( $platform = NULL ) {
+               $regex = '/^(\w*\|)*' . $this->db->databaseType . '(\|\w*)*$/';
+               
+               if( !isset( $platform ) OR preg_match( $regex, $platform ) ) {
+                       logMsg( "Platform $platform is supported" );
+                       return TRUE;
+               } else {
+                       logMsg( "Platform $platform is NOT supported" );
+                       return FALSE;
+               }
+       }
+       
+       /**
+       * Clears the array of generated SQL.
+       *
+       * @access private
+       */
+       function clearSQL() {
+               $this->sqlArray = array();
+       }
+       
+       /**
+       * Adds SQL into the SQL array.
+       *
+       * @param mixed $sql SQL to Add
+       * @return boolean TRUE if successful, else FALSE.
+       *
+       * @access private
+       */      
+       function addSQL( $sql = NULL ) {
+               if( is_array( $sql ) ) {
+                       foreach( $sql as $line ) {
+                               $this->addSQL( $line );
+                       }
+                       
+                       return TRUE;
+               }
+               
+               if( is_string( $sql ) ) {
+                       $this->sqlArray[] = $sql;
+                       
+                       // if executeInline is enabled, and either no errors have occurred or continueOnError is enabled, execute SQL.
+                       if( $this->ExecuteInline() && ( $this->success == 2 || $this->ContinueOnError() ) ) {
+                               $saved = $this->db->debug;
+                               $this->db->debug = $this->debug;
+                               $ok = $this->db->Execute( $sql );
+                               $this->db->debug = $saved;
+                               
+                               if( !$ok ) {
+                                       if( $this->debug ) {
+                                               ADOConnection::outp( $this->db->ErrorMsg() );
+                                       }
+                                       
+                                       $this->success = 1;
+                               }
+                       }
+                       
+                       return TRUE;
+               }
+               
+               return FALSE;
+       }
+       
+       /**
+       * Gets the SQL array in the specified format.
+       *
+       * @param string $format Format
+       * @return mixed SQL
+       *       
+       * @access private
+       */
+       function getSQL( $format = NULL, $sqlArray = NULL ) {
+               if( !is_array( $sqlArray ) ) {
+                       $sqlArray = $this->sqlArray;
+               }
+               
+               if( !is_array( $sqlArray ) ) {
+                       return FALSE;
+               }
+               
+               switch( strtolower( $format ) ) {
+                       case 'string':
+                       case 'text':
+                               return !empty( $sqlArray ) ? implode( ";\n\n", $sqlArray ) . ';' : '';
+                       case'html':
+                               return !empty( $sqlArray ) ? nl2br( htmlentities( implode( ";\n\n", $sqlArray ) . ';' ) ) : '';
+               }
+               
+               return $this->sqlArray;
+       }
+       
+       /**
+       * Destroys an adoSchema object.
+       *
+       * Call this method to clean up after an adoSchema object that is no longer in use.
+       * @deprecated adoSchema now cleans up automatically.
+       */
+       function Destroy() {
+               set_magic_quotes_runtime( $this->mgq );
+               unset( $this );
+       }
+}
+
+/**
+* Message logging function
+*
+* @access private
+*/
+function logMsg( $msg, $title = NULL, $force = FALSE ) {
+       if( XMLS_DEBUG or $force ) {
+               echo '<pre>';
+               
+               if( isset( $title ) ) {
+                       echo '<h3>' . htmlentities( $title ) . '</h3>';
+               }
+               
+               if( is_object( $this ) ) {
+                       echo '[' . get_class( $this ) . '] ';
+               }
+               
+               print_r( $msg );
+               
+               echo '</pre>';
+       }
+}
 ?>
\ No newline at end of file
index 8c6a538780e077e8a29969e270971ef8e2b2663b..f112f7b104c8d5cb331ab8eb0975ac1f3c7dc131 100644 (file)
-<?php\r
-// Copyright (c) 2004-2005 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$\r
- * @author Richard Tango-Lowy & Dan Cech\r
- * @version $Revision$\r
- *\r
- * @package axmls\r
- * @tutorial getting_started.pkg\r
- */\r
\r
-function _file_get_contents($file) \r
-{\r
-       if (function_exists('file_get_contents')) return file_get_contents($file);\r
-       \r
-       $f = fopen($file,'r');\r
-       if (!$f) return '';\r
-       $t = '';\r
-       \r
-       while ($s = fread($f,100000)) $t .= $s;\r
-       fclose($f);\r
-       return $t;\r
-}\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.3' );\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
-* How to handle data rows that already exist in a database during and upgrade.\r
-* Options are INSERT (attempts to insert duplicate rows), UPDATE (updates existing\r
-* rows) and IGNORE (ignores existing rows).\r
-*/\r
-if( !defined( 'XMLS_MODE_INSERT' ) ) {\r
-       define( 'XMLS_MODE_INSERT', 0 );\r
-}\r
-if( !defined( 'XMLS_MODE_UPDATE' ) ) {\r
-       define( 'XMLS_MODE_UPDATE', 1 );\r
-}\r
-if( !defined( 'XMLS_MODE_IGNORE' ) ) {\r
-       define( 'XMLS_MODE_IGNORE', 2 );\r
-}\r
-if( !defined( 'XMLS_EXISTING_DATA' ) ) {\r
-       define( 'XMLS_EXISTING_DATA', XMLS_MODE_INSERT );\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
-       * @var array Platform-specific options\r
-       * @access private\r
-       */\r
-       var $currentPlatform = true;\r
-       \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 = !empty( $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
-                       case 'DEFDATE':\r
-                       case 'DEFTIMESTAMP':\r
-                       case 'UNSIGNED':\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 'OPT':\r
-                       case 'CONSTRAINT':\r
-                               // Accept platform-specific options\r
-                               $this->currentPlatform = ( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) );\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/field 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/field option\r
-                       case 'OPT':\r
-                               if( isset( $this->current_field ) ) {\r
-                                       $this->addFieldOpt( $this->current_field, $cdata );\r
-                               } else {\r
-                               $this->addTableOpt( $cdata );\r
-                               }\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
-                       case 'OPT':\r
-                       case 'CONSTRAINT':\r
-                               $this->currentPlatform = true;\r
-                               break;\r
-                       default:\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'] = array($opts);\r
-               } else {\r
-                       $this->fields[$field_id]['OPTS'] = array();\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( $this->currentPlatform ) {\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
-       /**\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
-               if( $this->currentPlatform ) {\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, and is called\r
-* when we need to load field data into a table.\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 dbData 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: ROW and F (field). \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 insert\r
-       *\r
-       * @param string $name Field name\r
-       * @return string Field list\r
-       */\r
-       function addField( $attributes ) {\r
-               // check we're in a valid row\r
-               if( !isset( $this->row ) || !isset( $this->data[$this->row] ) ) {\r
-                       return;\r
-               }\r
-               \r
-               // Set the field index so we know where we are\r
-               if( isset( $attributes['NAME'] ) ) {\r
-                       $this->current_field = $this->FieldID( $attributes['NAME'] );\r
-               } else {\r
-                       $this->current_field = count( $this->data[$this->row] );\r
-               }\r
-               \r
-               // initialise data\r
-               if( !isset( $this->data[$this->row][$this->current_field] ) ) {\r
-                       $this->data[$this->row][$this->current_field] = '';\r
-               }\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
-               // check we're in a valid field\r
-               if ( isset( $this->data[$this->row][$this->current_field] ) ) {\r
-                       // add data to field\r
-                       $this->data[$this->row][$this->current_field] .= $cdata;\r
-               }\r
-       }\r
-       \r
-       /**\r
-       * Generates the SQL that will add/update the data 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
-               $tables = $xmls->db->MetaTables(); \r
-               $sql = array();\r
-               \r
-               $ukeys = $xmls->db->MetaPrimaryKeys( $table );\r
-               if( !empty( $this->parent->indexes ) and !empty( $ukeys ) ) {\r
-                       foreach( $this->parent->indexes as $indexObj ) {\r
-                               if( !in_array( $indexObj->name, $ukeys ) ) $ukeys[] = $indexObj->name;\r
-                       }\r
-               }\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
-                       $rawfields = array(); // Need to keep some of the unprocessed data on hand.\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 'I':\r
-                                       case 'I1':\r
-                                       case 'I2':\r
-                                       case 'I4':\r
-                                       case 'I8':\r
-                                               $fields[$name] = intval($field_data);\r
-                                               break;\r
-                                       case 'C':\r
-                                       case 'C2':\r
-                                       case 'X':\r
-                                       case 'X2':\r
-                                       default:\r
-                                               $fields[$name] = $xmls->db->qstr( $field_data );\r
-                                               $rawfields[$name] = $field_data;\r
-                               }\r
-                               \r
-                               unset($table_fields[$field_id]);\r
-                               \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'] ) and ( in_array( 'NOTNULL', $field['OPTS'] ) || in_array( 'KEY', $field['OPTS'] ) ) && !in_array( 'AUTOINCREMENT', $field['OPTS'] ) ) {\r
-                                                       continue(2);\r
-                                               }\r
-                               }\r
-                       }\r
-                       \r
-                       // The rest of this method deals with updating existing data records.\r
-                       \r
-                       if( !in_array( $table, $tables ) or ( $mode = $xmls->existingData() ) == XMLS_MODE_INSERT ) {\r
-                               // Table doesn't yet exist, so it's safe to insert.\r
-                               logMsg( "$table doesn't exist, inserting or mode is INSERT" );\r
-                       $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')';\r
-                               continue;\r
-               }\r
-               \r
-                       // Prepare to test for potential violations. Get primary keys and unique indexes\r
-                       $mfields = array_merge( $fields, $rawfields );\r
-                       $keyFields = array_intersect( $ukeys, array_keys( $mfields ) );\r
-                       \r
-                       if( empty( $ukeys ) or count( $keyFields ) == 0 ) {\r
-                               // No unique keys in schema, so safe to insert\r
-                               logMsg( "Either schema or data has no unique keys, so safe to insert" );\r
-                               $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')';\r
-                               continue;\r
-                       }\r
-                       \r
-                       // Select record containing matching unique keys.\r
-                       $where = '';\r
-                       foreach( $ukeys as $key ) {\r
-                               if( isset( $mfields[$key] ) and $mfields[$key] ) {\r
-                                       if( $where ) $where .= ' AND ';\r
-                                       $where .= $key . ' = ' . $xmls->db->qstr( $mfields[$key] );\r
-                               }\r
-                       }\r
-                       $records = $xmls->db->Execute( 'SELECT * FROM ' . $table . ' WHERE ' . $where );\r
-                       switch( $records->RecordCount() ) {\r
-                               case 0:\r
-                                       // No matching record, so safe to insert.\r
-                                       logMsg( "No matching records. Inserting new row with unique data" );\r
-                                       $sql[] = $xmls->db->GetInsertSQL( $records, $mfields );\r
-                                       break;\r
-                               case 1:\r
-                                       // Exactly one matching record, so we can update if the mode permits.\r
-                                       logMsg( "One matching record..." );\r
-                                       if( $mode == XMLS_MODE_UPDATE ) {\r
-                                               logMsg( "...Updating existing row from unique data" );\r
-                                               $sql[] = $xmls->db->GetUpdateSQL( $records, $mfields );\r
-                                       }\r
-                                       break;\r
-                               default:\r
-                                       // More than one matching record; the result is ambiguous, so we must ignore the row.\r
-                                       logMsg( "More than one matching record. Ignoring row." );\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$\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
-       * @var int      How to handle existing data rows (insert, update, or ignore)\r
-       */\r
-       var $existingData;\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->existingData( XMLS_EXISTING_DATA );\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
-       * Specifies how to handle existing data row when there is a unique key conflict.\r
-       *\r
-       * The existingData setting specifies how the parser should handle existing rows\r
-       * when a unique key violation occurs during the insert. This can happen when inserting\r
-       * data into an existing table with one or more primary keys or unique indexes.\r
-       * The existingData method takes one of three options: XMLS_MODE_INSERT attempts\r
-       * to always insert the data as a new row. In the event of a unique key violation,\r
-       * the database will generate an error.  XMLS_MODE_UPDATE attempts to update the \r
-       * any existing rows with the new data based upon primary or unique key fields in\r
-       * the schema. If the data row in the schema specifies no unique fields, the row\r
-       * data will be inserted as a new row. XMLS_MODE_IGNORE specifies that any data rows\r
-       * that would result in a unique key violation be ignored; no inserts or updates will\r
-       * take place. For backward compatibility, the default setting is XMLS_MODE_INSERT,\r
-       * but XMLS_MODE_UPDATE will generally be the most appropriate setting.\r
-       *\r
-       * @param int $mode XMLS_MODE_INSERT, XMLS_MODE_UPDATE, or XMLS_MODE_IGNORE\r
-       * @return int current mode\r
-       */\r
-       function ExistingData( $mode = NULL ) {\r
-               if( is_int( $mode ) ) {\r
-                       switch( $mode ) {\r
-                               case XMLS_MODE_UPDATE:\r
-                                       $mode = XMLS_MODE_UPDATE;\r
-                                       break;\r
-                               case XMLS_MODE_IGNORE:\r
-                                       $mode = XMLS_MODE_IGNORE;\r
-                                       break;\r
-                               case XMLS_MODE_INSERT:\r
-                                       $mode = XMLS_MODE_INSERT;\r
-                                       break;\r
-                               default:\r
-                                       $mode = XMLS_EXISITNG_DATA;\r
-                                       break;\r
-                       }\r
-                       $this->existingData = $mode;\r
-               }\r
-               \r
-               return $this->existingData;\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\r
-       * described. This method automatically converts the schema to the latest\r
-       * axmls schema version.\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 directly from a file (see\r
-       * the DTD for the proper format) and generate the SQL necessary to create\r
-       * the database described by the schema. Use this method when you are dealing\r
-       * with large schema files. Otherwise, ParseSchema() is faster.\r
-       * This method does not automatically convert the schema to the latest axmls\r
-       * schema version. You must convert the schema manually using either the\r
-       * ConvertSchemaFile() or ConvertSchemaString() method.\r
-       * @see ParseSchema()\r
-       * @see ConvertSchemaFile()\r
-       * @see ConvertSchemaString()\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
-                       logMsg( 'Unable to open file' );\r
-                       return FALSE;\r
-               }\r
-               \r
-               // do version detection here\r
-               if( $this->SchemaFileVersion( $filename ) != $this->schemaVersion ) {\r
-                       logMsg( 'Invalid Schema Version' );\r
-                       return FALSE;\r
-               }\r
-               \r
-               if( $returnSchema ) {\r
-                       $xmlstring = '';\r
-                       while( $data = fread( $fp, 4096 ) ) {\r
-                               $xmlstring .= $data . "\n";\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
-                       logMsg( 'Empty or Invalid Schema' );\r
-                       return FALSE;\r
-               }\r
-               \r
-               // do version detection here\r
-               if( $this->SchemaStringVersion( $xmlstring ) != $this->schemaVersion ) {\r
-                       logMsg( 'Invalid Schema Version' );\r
-                       return FALSE;\r
-               }\r
-               \r
-               if( $returnSchema ) {\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
-               $sqlArray = null;\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
-                               if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {\r
-                               $this->obj = new dbTable( $this, $attributes );\r
-                               xml_set_object( $parser, $this->obj );\r
-                               }\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
-       // compat for pre-4.3 - jlim\r
-       function _file_get_contents($path)\r
-       {\r
-               if (function_exists('file_get_contents')) return file_get_contents($path);\r
-               return join('',file($path));\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
-       * @indent string indentation to use\r
-       * @prefix string extract only tables with given prefix\r
-       * @stripprefix strip prefix string when storing in XML schema\r
-       * @return string Generated XML schema\r
-       */\r
-       function ExtractSchema( $data = FALSE, $indent = '  ', $prefix = '' , $stripprefix=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' , ($prefix) ? $prefix.'%' : '') ) ) {\r
-                       foreach( $tables as $table ) {\r
-                               if ($stripprefix) $table = str_replace(str_replace('\\_', '_', $pfx ), '', $table);\r
-                               $schema .= $indent . '<table name="' . htmlentities( $table ) . '">' . "\n";\r
-                               \r
-                               // grab details from database\r
-                               $rs = $this->db->Execute( 'SELECT * FROM ' . $table . ' WHERE -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( isset($details->max_length) && $details->max_length > 0 ) {\r
-                                                       $extra .= ' size="' . $details->max_length . '"';\r
-                                               }\r
-                                               \r
-                                               if( isset($details->primary_key) && $details->primary_key ) {\r
-                                                       $content[] = '<KEY/>';\r
-                                               } elseif( isset($details->not_null) && $details->not_null ) {\r
-                                                       $content[] = '<NOTNULL/>';\r
-                                               }\r
-                                               \r
-                                               if( isset($details->has_default) && $details->has_default ) {\r
-                                                       $content[] = '<DEFAULT value="' . htmlentities( $details->default_value ) . '"/>';\r
-                                               }\r
-                                               \r
-                                               if( isset($details->auto_increment) && $details->auto_increment ) {\r
-                                                       $content[] = '<AUTOINCREMENT/>';\r
-                                               }\r
-                                               \r
-                                               if( isset($details->unsigned) && $details->unsigned ) {\r
-                                                       $content[] = '<UNSIGNED/>';\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 .= str_repeat( $indent, 2 ) . '<field name="' . htmlentities( $details->name ) . '" type="' . $type . '"' . $extra;\r
-                                               \r
-                                               if( !empty( $content ) ) {\r
-                                                       $schema .= ">\n" . str_repeat( $indent, 3 )\r
-                                                                        . implode( "\n" . str_repeat( $indent, 3 ), $content ) . "\n"\r
-                                                                        . str_repeat( $indent, 2 ) . '</field>' . "\n";\r
-                                               } else {\r
-                                                       $schema .= "/>\n";\r
-                                               }\r
-                                       }\r
-                               }\r
-                               \r
-                               if( is_array( $indexes ) ) {\r
-                                       foreach( $indexes as $index => $details ) {\r
-                                               $schema .= str_repeat( $indent, 2 ) . '<index name="' . $index . '">' . "\n";\r
-                                               \r
-                                               if( $details['unique'] ) {\r
-                                                       $schema .= str_repeat( $indent, 3 ) . '<UNIQUE/>' . "\n";\r
-                                               }\r
-                                               \r
-                                               foreach( $details['columns'] as $column ) {\r
-                                                       $schema .= str_repeat( $indent, 3 ) . '<col>' . htmlentities( $column ) . '</col>' . "\n";\r
-                                               }\r
-                                               \r
-                                               $schema .= str_repeat( $indent, 2 ) . '</index>' . "\n";\r
-                                       }\r
-                               }\r
-                               \r
-                               if( $data ) {\r
-                                       $rs = $this->db->Execute( 'SELECT * FROM ' . $table );\r
-                                       \r
-                                       if( is_object( $rs ) && !$rs->EOF ) {\r
-                                               $schema .= str_repeat( $indent, 2 ) . "<data>\n";\r
-                                               \r
-                                               while( $row = $rs->FetchRow() ) {\r
-                                                       foreach( $row as $key => $val ) {\r
-                                                               if ( $val != htmlentities( $val ) ) {\r
-                                                                       $row[$key] = '<![CDATA[' . $val . ']]>';\r
-                                                               }\r
-                                                       }\r
-                                                       \r
-                                                       $schema .= str_repeat( $indent, 3 ) . '<row><f>' . implode( '</f><f>', $row ) . "</f></row>\n";\r
-                                               }\r
-                                               \r
-                                               $schema .= str_repeat( $indent, 2 ) . "</data>\n";\r
-                                       }\r
-                               }\r
-                               \r
-                               $schema .= $indent . "</table>\n";\r
-                       }\r
-               }\r
-               \r
-               $this->db->SetFetchMode( $old_mode );\r
-               \r
-               $schema .= '</schema>';\r
-               return $schema;\r
-       }\r
-       \r
-       /**\r
-       * Sets a prefix for database objects\r
-       *\r
-       * Call this method to set a standard prefix that will be prepended to all database tables \r
-       * and indices when the schema is parsed. Calling setPrefix with no arguments clears the prefix.\r
-       *\r
-       * @param string $prefix Prefix that will be prepended.\r
-       * @param boolean $underscore If TRUE, automatically append an underscore character to the prefix.\r
-       * @return boolean TRUE if successful, else FALSE\r
-       */\r
-       function SetPrefix( $prefix = '', $underscore = TRUE ) {\r
-               switch( TRUE ) {\r
-                       // clear prefix\r
-                       case empty( $prefix ):\r
-                               logMsg( 'Cleared prefix' );\r
-                               $this->objectPrefix = '';\r
-                               return TRUE;\r
-                       // prefix too long\r
-                       case strlen( $prefix ) > XMLS_PREFIX_MAXLEN:\r
-                       // prefix contains invalid characters\r
-                       case !preg_match( '/^[a-z][a-z0-9_]+$/i', $prefix ):\r
-                               logMsg( 'Invalid prefix: ' . $prefix );\r
-                               return FALSE;\r
-               }\r
-               \r
-               if( $underscore AND substr( $prefix, -1 ) != '_' ) {\r
-                       $prefix .= '_';\r
-               }\r
-               \r
-               // prefix valid\r
-               logMsg( 'Set prefix: ' . $prefix );\r
-               $this->objectPrefix = $prefix;\r
-               return TRUE;\r
-       }\r
-       \r
-       /**\r
-       * Returns an object name with the current prefix prepended.\r
-       *\r
-       * @param string $name Name\r
-       * @return string        Prefixed name\r
-       *\r
-       * @access private\r
-       */\r
-       function prefix( $name = '' ) {\r
-               // if prefix is set\r
-               if( !empty( $this->objectPrefix ) ) {\r
-                       // Prepend the object prefix to the table name\r
-                       // prepend after quote if used\r
-                       return preg_replace( '/^(`?)(.+)$/', '$1' . $this->objectPrefix . '$2', $name );\r
-               }\r
-               \r
-               // No prefix set. Use name provided.\r
-               return $name;\r
-       }\r
-       \r
-       /**\r
-       * Checks if element references a specific platform\r
-       *\r
-       * @param string $platform Requested platform\r
-       * @returns boolean TRUE if platform check succeeds\r
-       *\r
-       * @access private\r
-       */\r
-       function supportedPlatform( $platform = NULL ) {\r
-               if( !empty( $platform ) ) {\r
-                       $regex = '/(^|\|)' . $this->db->databaseType . '(\||$)/i';\r
-               \r
-                       if( preg_match( '/^- /', $platform ) ) {\r
-                               if (preg_match ( $regex, substr( $platform, 2 ) ) ) {\r
-                                       logMsg( 'Platform ' . $platform . ' is NOT supported' );\r
-                                       return FALSE;\r
-                               }\r
-               } else {\r
-                               if( !preg_match ( $regex, $platform ) ) {\r
-                                       logMsg( 'Platform ' . $platform . ' is NOT supported' );\r
-                       return FALSE;\r
-               }\r
-       }\r
-               }\r
-               \r
-               logMsg( 'Platform ' . $platform . ' is supported' );\r
-               return TRUE;\r
-       }\r
-       \r
-       /**\r
-       * Clears the array of generated SQL.\r
-       *\r
-       * @access private\r
-       */\r
-       function clearSQL() {\r
-               $this->sqlArray = array();\r
-       }\r
-       \r
-       /**\r
-       * Adds SQL into the SQL array.\r
-       *\r
-       * @param mixed $sql SQL to Add\r
-       * @return boolean TRUE if successful, else FALSE.\r
-       *\r
-       * @access private\r
-       */      \r
-       function addSQL( $sql = NULL ) {\r
-               if( is_array( $sql ) ) {\r
-                       foreach( $sql as $line ) {\r
-                               $this->addSQL( $line );\r
-                       }\r
-                       \r
-                       return TRUE;\r
-               }\r
-               \r
-               if( is_string( $sql ) ) {\r
-                       $this->sqlArray[] = $sql;\r
-                       \r
-                       // if executeInline is enabled, and either no errors have occurred or continueOnError is enabled, execute SQL.\r
-                       if( $this->ExecuteInline() && ( $this->success == 2 || $this->ContinueOnError() ) ) {\r
-                               $saved = $this->db->debug;\r
-                               $this->db->debug = $this->debug;\r
-                               $ok = $this->db->Execute( $sql );\r
-                               $this->db->debug = $saved;\r
-                               \r
-                               if( !$ok ) {\r
-                                       if( $this->debug ) {\r
-                                               ADOConnection::outp( $this->db->ErrorMsg() );\r
-                                       }\r
-                                       \r
-                                       $this->success = 1;\r
-                               }\r
-                       }\r
-                       \r
-                       return TRUE;\r
-               }\r
-               \r
-               return FALSE;\r
-       }\r
-       \r
-       /**\r
-       * Gets the SQL array in the specified format.\r
-       *\r
-       * @param string $format Format\r
-       * @return mixed SQL\r
-       *       \r
-       * @access private\r
-       */\r
-       function getSQL( $format = NULL, $sqlArray = NULL ) {\r
-               if( !is_array( $sqlArray ) ) {\r
-                       $sqlArray = $this->sqlArray;\r
-               }\r
-               \r
-               if( !is_array( $sqlArray ) ) {\r
-                       return FALSE;\r
-               }\r
-               \r
-               switch( strtolower( $format ) ) {\r
-                       case 'string':\r
-                       case 'text':\r
-                               return !empty( $sqlArray ) ? implode( ";\n\n", $sqlArray ) . ';' : '';\r
-                       case'html':\r
-                               return !empty( $sqlArray ) ? nl2br( htmlentities( implode( ";\n\n", $sqlArray ) . ';' ) ) : '';\r
-               }\r
-               \r
-               return $this->sqlArray;\r
-       }\r
-       \r
-       /**\r
-       * Destroys an adoSchema object.\r
-       *\r
-       * Call this method to clean up after an adoSchema object that is no longer in use.\r
-       * @deprecated adoSchema now cleans up automatically.\r
-       */\r
-       function Destroy() {\r
-               set_magic_quotes_runtime( $this->mgq );\r
-               unset( $this );\r
-       }\r
-}\r
-\r
-/**\r
-* Message logging function\r
-*\r
-* @access private\r
-*/\r
-function logMsg( $msg, $title = NULL, $force = FALSE ) {\r
-       if( XMLS_DEBUG or $force ) {\r
-               echo '<pre>';\r
-               \r
-               if( isset( $title ) ) {\r
-                       echo '<h3>' . htmlentities( $title ) . '</h3>';\r
-               }\r
-               \r
-               if( @is_object( $this ) ) {\r
-                       echo '[' . get_class( $this ) . '] ';\r
-               }\r
-               \r
-               print_r( $msg );\r
-               \r
-               echo '</pre>';\r
-       }\r
-}\r
+<?php
+// Copyright (c) 2004-2005 ars Cognita Inc., 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. 
+*******************************************************************************/
+/**
+ * xmlschema is a class that allows the user to quickly and easily
+ * build a database on any ADOdb-supported platform using a simple
+ * XML schema.
+ *
+ * Last Editor: $Author$
+ * @author Richard Tango-Lowy & Dan Cech
+ * @version $Revision$
+ *
+ * @package axmls
+ * @tutorial getting_started.pkg
+ */
+function _file_get_contents($file) 
+{
+       if (function_exists('file_get_contents')) return file_get_contents($file);
+       
+       $f = fopen($file,'r');
+       if (!$f) return '';
+       $t = '';
+       
+       while ($s = fread($f,100000)) $t .= $s;
+       fclose($f);
+       return $t;
+}
+
+
+/**
+* Debug on or off
+*/
+if( !defined( 'XMLS_DEBUG' ) ) {
+       define( 'XMLS_DEBUG', FALSE );
+}
+
+/**
+* Default prefix key
+*/
+if( !defined( 'XMLS_PREFIX' ) ) {
+       define( 'XMLS_PREFIX', '%%P' );
+}
+
+/**
+* Maximum length allowed for object prefix
+*/
+if( !defined( 'XMLS_PREFIX_MAXLEN' ) ) {
+       define( 'XMLS_PREFIX_MAXLEN', 10 );
+}
+
+/**
+* Execute SQL inline as it is generated
+*/
+if( !defined( 'XMLS_EXECUTE_INLINE' ) ) {
+       define( 'XMLS_EXECUTE_INLINE', FALSE );
+}
+
+/**
+* Continue SQL Execution if an error occurs?
+*/
+if( !defined( 'XMLS_CONTINUE_ON_ERROR' ) ) {
+       define( 'XMLS_CONTINUE_ON_ERROR', FALSE );
+}
+
+/**
+* Current Schema Version
+*/
+if( !defined( 'XMLS_SCHEMA_VERSION' ) ) {
+       define( 'XMLS_SCHEMA_VERSION', '0.3' );
+}
+
+/**
+* Default Schema Version.  Used for Schemas without an explicit version set.
+*/
+if( !defined( 'XMLS_DEFAULT_SCHEMA_VERSION' ) ) {
+       define( 'XMLS_DEFAULT_SCHEMA_VERSION', '0.1' );
+}
+
+/**
+* How to handle data rows that already exist in a database during and upgrade.
+* Options are INSERT (attempts to insert duplicate rows), UPDATE (updates existing
+* rows) and IGNORE (ignores existing rows).
+*/
+if( !defined( 'XMLS_MODE_INSERT' ) ) {
+       define( 'XMLS_MODE_INSERT', 0 );
+}
+if( !defined( 'XMLS_MODE_UPDATE' ) ) {
+       define( 'XMLS_MODE_UPDATE', 1 );
+}
+if( !defined( 'XMLS_MODE_IGNORE' ) ) {
+       define( 'XMLS_MODE_IGNORE', 2 );
+}
+if( !defined( 'XMLS_EXISTING_DATA' ) ) {
+       define( 'XMLS_EXISTING_DATA', XMLS_MODE_INSERT );
+}
+
+/**
+* Default Schema Version.  Used for Schemas without an explicit version set.
+*/
+if( !defined( 'XMLS_DEFAULT_UPGRADE_METHOD' ) ) {
+       define( 'XMLS_DEFAULT_UPGRADE_METHOD', 'ALTER' );
+}
+
+/**
+* Include the main ADODB library
+*/
+if( !defined( '_ADODB_LAYER' ) ) {
+       require( 'adodb.inc.php' );
+       require( 'adodb-datadict.inc.php' );
+}
+
+/**
+* Abstract DB Object. This class provides basic methods for database objects, such
+* as tables and indexes.
+*
+* @package axmls
+* @access private
+*/
+class dbObject {
+       
+       /**
+       * var object Parent
+       */
+       var $parent;
+       
+       /**
+       * var string current element
+       */
+       var $currentElement;
+       
+       /**
+       * NOP
+       */
+       function dbObject( &$parent, $attributes = NULL ) {
+               $this->parent = $parent;
+       }
+       
+       /**
+       * XML Callback to process start elements
+       *
+       * @access private
+       */
+       function _tag_open( &$parser, $tag, $attributes ) {
+               
+       }
+       
+       /**
+       * XML Callback to process CDATA elements
+       *
+       * @access private
+       */
+       function _tag_cdata( &$parser, $cdata ) {
+               
+       }
+       
+       /**
+       * XML Callback to process end elements
+       *
+       * @access private
+       */
+       function _tag_close( &$parser, $tag ) {
+               
+       }
+       
+       function create() {
+               return array();
+       }
+       
+       /**
+       * Destroys the object
+       */
+       function destroy() {
+               unset( $this );
+       }
+       
+       /**
+       * Checks whether the specified RDBMS is supported by the current
+       * database object or its ranking ancestor.
+       *
+       * @param string $platform RDBMS platform name (from ADODB platform list).
+       * @return boolean TRUE if RDBMS is supported; otherwise returns FALSE.
+       */
+       function supportedPlatform( $platform = NULL ) {
+               return is_object( $this->parent ) ? $this->parent->supportedPlatform( $platform ) : TRUE;
+       }
+       
+       /**
+       * Returns the prefix set by the ranking ancestor of the database object.
+       *
+       * @param string $name Prefix string.
+       * @return string Prefix.
+       */
+       function prefix( $name = '' ) {
+               return is_object( $this->parent ) ? $this->parent->prefix( $name ) : $name;
+       }
+       
+       /**
+       * Extracts a field ID from the specified field.
+       *
+       * @param string $field Field.
+       * @return string Field ID.
+       */
+       function FieldID( $field ) {
+               return strtoupper( preg_replace( '/^`(.+)`$/', '$1', $field ) );
+       }
+}
+
+/**
+* Creates a table object in ADOdb's datadict format
+*
+* This class stores information about a database table. As charactaristics
+* of the table are loaded from the external source, methods and properties
+* of this class are used to build up the table description in ADOdb's
+* datadict format.
+*
+* @package axmls
+* @access private
+*/
+class dbTable extends dbObject {
+       
+       /**
+       * @var string Table name
+       */
+       var $name;
+       
+       /**
+       * @var array Field specifier: Meta-information about each field
+       */
+       var $fields = array();
+       
+       /**
+       * @var array List of table indexes.
+       */
+       var $indexes = array();
+       
+       /**
+       * @var array Table options: Table-level options
+       */
+       var $opts = array();
+       
+       /**
+       * @var string Field index: Keeps track of which field is currently being processed
+       */
+       var $current_field;
+       
+       /**
+       * @var boolean Mark table for destruction
+       * @access private
+       */
+       var $drop_table;
+       
+       /**
+       * @var boolean Mark field for destruction (not yet implemented)
+       * @access private
+       */
+       var $drop_field = array();
+       
+       /**
+       * @var array Platform-specific options
+       * @access private
+       */
+       var $currentPlatform = true;
+       
+       
+       /**
+       * Iniitializes a new table object.
+       *
+       * @param string $prefix DB Object prefix
+       * @param array $attributes Array of table attributes.
+       */
+       function dbTable( &$parent, $attributes = NULL ) {
+               $this->parent = $parent;
+               $this->name = $this->prefix($attributes['NAME']);
+       }
+       
+       /**
+       * XML Callback to process start elements. Elements currently 
+       * processed are: INDEX, DROP, FIELD, KEY, NOTNULL, AUTOINCREMENT & DEFAULT. 
+       *
+       * @access private
+       */
+       function _tag_open( &$parser, $tag, $attributes ) {
+               $this->currentElement = strtoupper( $tag );
+               
+               switch( $this->currentElement ) {
+                       case 'INDEX':
+                               if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
+                                       xml_set_object( $parser, $this->addIndex( $attributes ) );
+                               }
+                               break;
+                       case 'DATA':
+                               if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
+                                       xml_set_object( $parser, $this->addData( $attributes ) );
+                               }
+                               break;
+                       case 'DROP':
+                               $this->drop();
+                               break;
+                       case 'FIELD':
+                               // Add a field
+                               $fieldName = $attributes['NAME'];
+                               $fieldType = $attributes['TYPE'];
+                               $fieldSize = isset( $attributes['SIZE'] ) ? $attributes['SIZE'] : NULL;
+                               $fieldOpts = !empty( $attributes['OPTS'] ) ? $attributes['OPTS'] : NULL;
+                               
+                               $this->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts );
+                               break;
+                       case 'KEY':
+                       case 'NOTNULL':
+                       case 'AUTOINCREMENT':
+                       case 'DEFDATE':
+                       case 'DEFTIMESTAMP':
+                       case 'UNSIGNED':
+                               // Add a field option
+                               $this->addFieldOpt( $this->current_field, $this->currentElement );
+                               break;
+                       case 'DEFAULT':
+                               // Add a field option to the table object
+                               
+                               // Work around ADOdb datadict issue that misinterprets empty strings.
+                               if( $attributes['VALUE'] == '' ) {
+                                       $attributes['VALUE'] = " '' ";
+                               }
+                               
+                               $this->addFieldOpt( $this->current_field, $this->currentElement, $attributes['VALUE'] );
+                               break;
+                       case 'OPT':
+                       case 'CONSTRAINT':
+                               // Accept platform-specific options
+                               $this->currentPlatform = ( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) );
+                               break;
+                       default:
+                               // print_r( array( $tag, $attributes ) );
+               }
+       }
+       
+       /**
+       * XML Callback to process CDATA elements
+       *
+       * @access private
+       */
+       function _tag_cdata( &$parser, $cdata ) {
+               switch( $this->currentElement ) {
+                       // Table/field constraint
+                       case 'CONSTRAINT':
+                               if( isset( $this->current_field ) ) {
+                                       $this->addFieldOpt( $this->current_field, $this->currentElement, $cdata );
+                               } else {
+                                       $this->addTableOpt( $cdata );
+                               }
+                               break;
+                       // Table/field option
+                       case 'OPT':
+                               if( isset( $this->current_field ) ) {
+                                       $this->addFieldOpt( $this->current_field, $cdata );
+                               } else {
+                               $this->addTableOpt( $cdata );
+                               }
+                               break;
+                       default:
+                               
+               }
+       }
+       
+       /**
+       * XML Callback to process end elements
+       *
+       * @access private
+       */
+       function _tag_close( &$parser, $tag ) {
+               $this->currentElement = '';
+               
+               switch( strtoupper( $tag ) ) {
+                       case 'TABLE':
+                               $this->parent->addSQL( $this->create( $this->parent ) );
+                               xml_set_object( $parser, $this->parent );
+                               $this->destroy();
+                               break;
+                       case 'FIELD':
+                               unset($this->current_field);
+                               break;
+                       case 'OPT':
+                       case 'CONSTRAINT':
+                               $this->currentPlatform = true;
+                               break;
+                       default:
+
+               }
+       }
+       
+       /**
+       * Adds an index to a table object
+       *
+       * @param array $attributes Index attributes
+       * @return object dbIndex object
+       */
+       function addIndex( $attributes ) {
+               $name = strtoupper( $attributes['NAME'] );
+               $this->indexes[$name] = new dbIndex( $this, $attributes );
+               return $this->indexes[$name];
+       }
+       
+       /**
+       * Adds data to a table object
+       *
+       * @param array $attributes Data attributes
+       * @return object dbData object
+       */
+       function addData( $attributes ) {
+               if( !isset( $this->data ) ) {
+                       $this->data = new dbData( $this, $attributes );
+               }
+               return $this->data;
+       }
+       
+       /**
+       * Adds a field to a table object
+       *
+       * $name is the name of the table to which the field should be added. 
+       * $type is an ADODB datadict field type. The following field types
+       * are supported as of ADODB 3.40:
+       *       - C:  varchar
+       *       - X:  CLOB (character large object) or largest varchar size
+       *          if CLOB is not supported
+       *       - C2: Multibyte varchar
+       *       - X2: Multibyte CLOB
+       *       - B:  BLOB (binary large object)
+       *       - D:  Date (some databases do not support this, and we return a datetime type)
+       *       - T:  Datetime or Timestamp
+       *       - L:  Integer field suitable for storing booleans (0 or 1)
+       *       - I:  Integer (mapped to I4)
+       *       - I1: 1-byte integer
+       *       - I2: 2-byte integer
+       *       - I4: 4-byte integer
+       *       - I8: 8-byte integer
+       *       - F:  Floating point number
+       *       - N:  Numeric or decimal number
+       *
+       * @param string $name Name of the table to which the field will be added.
+       * @param string $type   ADODB datadict field type.
+       * @param string $size   Field size
+       * @param array $opts    Field options array
+       * @return array Field specifier array
+       */
+       function addField( $name, $type, $size = NULL, $opts = NULL ) {
+               $field_id = $this->FieldID( $name );
+               
+               // Set the field index so we know where we are
+               $this->current_field = $field_id;
+               
+               // Set the field name (required)
+               $this->fields[$field_id]['NAME'] = $name;
+               
+               // Set the field type (required)
+               $this->fields[$field_id]['TYPE'] = $type;
+               
+               // Set the field size (optional)
+               if( isset( $size ) ) {
+                       $this->fields[$field_id]['SIZE'] = $size;
+               }
+               
+               // Set the field options
+               if( isset( $opts ) ) {
+                       $this->fields[$field_id]['OPTS'] = array($opts);
+               } else {
+                       $this->fields[$field_id]['OPTS'] = array();
+               }
+       }
+       
+       /**
+       * Adds a field option to the current field specifier
+       *
+       * This method adds a field option allowed by the ADOdb datadict 
+       * and appends it to the given field.
+       *
+       * @param string $field  Field name
+       * @param string $opt ADOdb field option
+       * @param mixed $value Field option value
+       * @return array Field specifier array
+       */
+       function addFieldOpt( $field, $opt, $value = NULL ) {
+               if( $this->currentPlatform ) {
+               if( !isset( $value ) ) {
+                       $this->fields[$this->FieldID( $field )]['OPTS'][] = $opt;
+               // Add the option and value
+               } else {
+                       $this->fields[$this->FieldID( $field )]['OPTS'][] = array( $opt => $value );
+               }
+       }
+       }
+       
+       /**
+       * Adds an option to the table
+       *
+       * This method takes a comma-separated list of table-level options
+       * and appends them to the table object.
+       *
+       * @param string $opt Table option
+       * @return array Options
+       */
+       function addTableOpt( $opt ) {
+               if(isset($this->currentPlatform)) {
+                       $this->opts[$this->parent->db->databaseType] = $opt;
+               }
+               return $this->opts;
+       }
+
+       
+       /**
+       * Generates the SQL that will create the table in the database
+       *
+       * @param object $xmls adoSchema object
+       * @return array Array containing table creation SQL
+       */
+       function create( &$xmls ) {
+               $sql = array();
+               
+               // drop any existing indexes
+               if( is_array( $legacy_indexes = $xmls->dict->MetaIndexes( $this->name ) ) ) {
+                       foreach( $legacy_indexes as $index => $index_details ) {
+                               $sql[] = $xmls->dict->DropIndexSQL( $index, $this->name );
+                       }
+               }
+               
+               // remove fields to be dropped from table object
+               foreach( $this->drop_field as $field ) {
+                       unset( $this->fields[$field] );
+               }
+               
+               // if table exists
+               if( is_array( $legacy_fields = $xmls->dict->MetaColumns( $this->name ) ) ) {
+                       // drop table
+                       if( $this->drop_table ) {
+                               $sql[] = $xmls->dict->DropTableSQL( $this->name );
+                               
+                               return $sql;
+                       }
+                       
+                       // drop any existing fields not in schema
+                       foreach( $legacy_fields as $field_id => $field ) {
+                               if( !isset( $this->fields[$field_id] ) ) {
+                                       $sql[] = $xmls->dict->DropColumnSQL( $this->name, $field->name );
+                               }
+                       }
+               // if table doesn't exist
+               } else {
+                       if( $this->drop_table ) {
+                               return $sql;
+                       }
+                       
+                       $legacy_fields = array();
+               }
+               
+               // Loop through the field specifier array, building the associative array for the field options
+               $fldarray = array();
+               
+               foreach( $this->fields as $field_id => $finfo ) {
+                       // Set an empty size if it isn't supplied
+                       if( !isset( $finfo['SIZE'] ) ) {
+                               $finfo['SIZE'] = '';
+                       }
+                       
+                       // Initialize the field array with the type and size
+                       $fldarray[$field_id] = array(
+                               'NAME' => $finfo['NAME'],
+                               'TYPE' => $finfo['TYPE'],
+                               'SIZE' => $finfo['SIZE']
+                       );
+                       
+                       // Loop through the options array and add the field options. 
+                       if( isset( $finfo['OPTS'] ) ) {
+                               foreach( $finfo['OPTS'] as $opt ) {
+                                       // Option has an argument.
+                                       if( is_array( $opt ) ) {
+                                               $key = key( $opt );
+                                               $value = $opt[key( $opt )];
+                                               @$fldarray[$field_id][$key] .= $value;
+                                       // Option doesn't have arguments
+                                       } else {
+                                               $fldarray[$field_id][$opt] = $opt;
+                                       }
+                               }
+                       }
+               }
+               
+               if( empty( $legacy_fields ) ) {
+                       // Create the new table
+                       $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
+                       logMsg( end( $sql ), 'Generated CreateTableSQL' );
+               } else {
+                       // Upgrade an existing table
+                       logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" );
+                       switch( $xmls->upgrade ) {
+                               // Use ChangeTableSQL
+                               case 'ALTER':
+                                       logMsg( 'Generated ChangeTableSQL (ALTERing table)' );
+                                       $sql[] = $xmls->dict->ChangeTableSQL( $this->name, $fldarray, $this->opts );
+                                       break;
+                               case 'REPLACE':
+                                       logMsg( 'Doing upgrade REPLACE (testing)' );
+                                       $sql[] = $xmls->dict->DropTableSQL( $this->name );
+                                       $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
+                                       break;
+                               // ignore table
+                               default:
+                                       return array();
+                       }
+               }
+               
+               foreach( $this->indexes as $index ) {
+                       $sql[] = $index->create( $xmls );
+               }
+               
+               if( isset( $this->data ) ) {
+                       $sql[] = $this->data->create( $xmls );
+               }
+               
+               return $sql;
+       }
+       
+       /**
+       * Marks a field or table for destruction
+       */
+       function drop() {
+               if( isset( $this->current_field ) ) {
+                       // Drop the current field
+                       logMsg( "Dropping field '{$this->current_field}' from table '{$this->name}'" );
+                       // $this->drop_field[$this->current_field] = $xmls->dict->DropColumnSQL( $this->name, $this->current_field );
+                       $this->drop_field[$this->current_field] = $this->current_field;
+               } else {
+                       // Drop the current table
+                       logMsg( "Dropping table '{$this->name}'" );
+                       // $this->drop_table = $xmls->dict->DropTableSQL( $this->name );
+                       $this->drop_table = TRUE;
+               }
+       }
+}
+
+/**
+* Creates an index object in ADOdb's datadict format
+*
+* This class stores information about a database index. As charactaristics
+* of the index are loaded from the external source, methods and properties
+* of this class are used to build up the index description in ADOdb's
+* datadict format.
+*
+* @package axmls
+* @access private
+*/
+class dbIndex extends dbObject {
+       
+       /**
+       * @var string   Index name
+       */
+       var $name;
+       
+       /**
+       * @var array    Index options: Index-level options
+       */
+       var $opts = array();
+       
+       /**
+       * @var array    Indexed fields: Table columns included in this index
+       */
+       var $columns = array();
+       
+       /**
+       * @var boolean Mark index for destruction
+       * @access private
+       */
+       var $drop = FALSE;
+       
+       /**
+       * Initializes the new dbIndex object.
+       *
+       * @param object $parent Parent object
+       * @param array $attributes Attributes
+       *
+       * @internal
+       */
+       function dbIndex( &$parent, $attributes = NULL ) {
+               $this->parent = $parent;
+               
+               $this->name = $this->prefix ($attributes['NAME']);
+       }
+       
+       /**
+       * XML Callback to process start elements
+       *
+       * Processes XML opening tags. 
+       * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH. 
+       *
+       * @access private
+       */
+       function _tag_open( &$parser, $tag, $attributes ) {
+               $this->currentElement = strtoupper( $tag );
+               
+               switch( $this->currentElement ) {
+                       case 'DROP':
+                               $this->drop();
+                               break;
+                       case 'CLUSTERED':
+                       case 'BITMAP':
+                       case 'UNIQUE':
+                       case 'FULLTEXT':
+                       case 'HASH':
+                               // Add index Option
+                               $this->addIndexOpt( $this->currentElement );
+                               break;
+                       default:
+                               // print_r( array( $tag, $attributes ) );
+               }
+       }
+       
+       /**
+       * XML Callback to process CDATA elements
+       *
+       * Processes XML cdata.
+       *
+       * @access private
+       */
+       function _tag_cdata( &$parser, $cdata ) {
+               switch( $this->currentElement ) {
+                       // Index field name
+                       case 'COL':
+                               $this->addField( $cdata );
+                               break;
+                       default:
+                               
+               }
+       }
+       
+       /**
+       * XML Callback to process end elements
+       *
+       * @access private
+       */
+       function _tag_close( &$parser, $tag ) {
+               $this->currentElement = '';
+               
+               switch( strtoupper( $tag ) ) {
+                       case 'INDEX':
+                               xml_set_object( $parser, $this->parent );
+                               break;
+               }
+       }
+       
+       /**
+       * Adds a field to the index
+       *
+       * @param string $name Field name
+       * @return string Field list
+       */
+       function addField( $name ) {
+               $this->columns[$this->FieldID( $name )] = $name;
+               
+               // Return the field list
+               return $this->columns;
+       }
+       
+       /**
+       * Adds options to the index
+       *
+       * @param string $opt Comma-separated list of index options.
+       * @return string Option list
+       */
+       function addIndexOpt( $opt ) {
+               $this->opts[] = $opt;
+               
+               // Return the options list
+               return $this->opts;
+       }
+       
+       /**
+       * Generates the SQL that will create the index in the database
+       *
+       * @param object $xmls adoSchema object
+       * @return array Array containing index creation SQL
+       */
+       function create( &$xmls ) {
+               if( $this->drop ) {
+                       return NULL;
+               }
+               
+               // eliminate any columns that aren't in the table
+               foreach( $this->columns as $id => $col ) {
+                       if( !isset( $this->parent->fields[$id] ) ) {
+                               unset( $this->columns[$id] );
+                       }
+               }
+               
+               return $xmls->dict->CreateIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts );
+       }
+       
+       /**
+       * Marks an index for destruction
+       */
+       function drop() {
+               $this->drop = TRUE;
+       }
+}
+
+/**
+* Creates a data object in ADOdb's datadict format
+*
+* This class stores information about table data, and is called
+* when we need to load field data into a table.
+*
+* @package axmls
+* @access private
+*/
+class dbData extends dbObject {
+       
+       var $data = array();
+       
+       var $row;
+       
+       /**
+       * Initializes the new dbData object.
+       *
+       * @param object $parent Parent object
+       * @param array $attributes Attributes
+       *
+       * @internal
+       */
+       function dbData( &$parent, $attributes = NULL ) {
+               $this->parent = $parent;
+       }
+       
+       /**
+       * XML Callback to process start elements
+       *
+       * Processes XML opening tags. 
+       * Elements currently processed are: ROW and F (field). 
+       *
+       * @access private
+       */
+       function _tag_open( &$parser, $tag, $attributes ) {
+               $this->currentElement = strtoupper( $tag );
+               
+               switch( $this->currentElement ) {
+                       case 'ROW':
+                               $this->row = count( $this->data );
+                               $this->data[$this->row] = array();
+                               break;
+                       case 'F':
+                               $this->addField($attributes);
+                       default:
+                               // print_r( array( $tag, $attributes ) );
+               }
+       }
+       
+       /**
+       * XML Callback to process CDATA elements
+       *
+       * Processes XML cdata.
+       *
+       * @access private
+       */
+       function _tag_cdata( &$parser, $cdata ) {
+               switch( $this->currentElement ) {
+                       // Index field name
+                       case 'F':
+                               $this->addData( $cdata );
+                               break;
+                       default:
+                               
+               }
+       }
+       
+       /**
+       * XML Callback to process end elements
+       *
+       * @access private
+       */
+       function _tag_close( &$parser, $tag ) {
+               $this->currentElement = '';
+               
+               switch( strtoupper( $tag ) ) {
+                       case 'DATA':
+                               xml_set_object( $parser, $this->parent );
+                               break;
+               }
+       }
+       
+       /**
+       * Adds a field to the insert
+       *
+       * @param string $name Field name
+       * @return string Field list
+       */
+       function addField( $attributes ) {
+               // check we're in a valid row
+               if( !isset( $this->row ) || !isset( $this->data[$this->row] ) ) {
+                       return;
+               }
+               
+               // Set the field index so we know where we are
+               if( isset( $attributes['NAME'] ) ) {
+                       $this->current_field = $this->FieldID( $attributes['NAME'] );
+               } else {
+                       $this->current_field = count( $this->data[$this->row] );
+               }
+               
+               // initialise data
+               if( !isset( $this->data[$this->row][$this->current_field] ) ) {
+                       $this->data[$this->row][$this->current_field] = '';
+               }
+       }
+       
+       /**
+       * Adds options to the index
+       *
+       * @param string $opt Comma-separated list of index options.
+       * @return string Option list
+       */
+       function addData( $cdata ) {
+               // check we're in a valid field
+               if ( isset( $this->data[$this->row][$this->current_field] ) ) {
+                       // add data to field
+                       $this->data[$this->row][$this->current_field] .= $cdata;
+               }
+       }
+       
+       /**
+       * Generates the SQL that will add/update the data in the database
+       *
+       * @param object $xmls adoSchema object
+       * @return array Array containing index creation SQL
+       */
+       function create( &$xmls ) {
+               $table = $xmls->dict->TableName($this->parent->name);
+               $table_field_count = count($this->parent->fields);
+               $tables = $xmls->db->MetaTables(); 
+               $sql = array();
+               
+               $ukeys = $xmls->db->MetaPrimaryKeys( $table );
+               if( !empty( $this->parent->indexes ) and !empty( $ukeys ) ) {
+                       foreach( $this->parent->indexes as $indexObj ) {
+                               if( !in_array( $indexObj->name, $ukeys ) ) $ukeys[] = $indexObj->name;
+                       }
+               }
+               
+               // eliminate any columns that aren't in the table
+               foreach( $this->data as $row ) {
+                       $table_fields = $this->parent->fields;
+                       $fields = array();
+                       $rawfields = array(); // Need to keep some of the unprocessed data on hand.
+                       
+                       foreach( $row as $field_id => $field_data ) {
+                               if( !array_key_exists( $field_id, $table_fields ) ) {
+                                       if( is_numeric( $field_id ) ) {
+                                               $field_id = reset( array_keys( $table_fields ) );
+                                       } else {
+                                               continue;
+                                       }
+                               }
+                               
+                               $name = $table_fields[$field_id]['NAME'];
+                               
+                               switch( $table_fields[$field_id]['TYPE'] ) {
+                                       case 'I':
+                                       case 'I1':
+                                       case 'I2':
+                                       case 'I4':
+                                       case 'I8':
+                                               $fields[$name] = intval($field_data);
+                                               break;
+                                       case 'C':
+                                       case 'C2':
+                                       case 'X':
+                                       case 'X2':
+                                       default:
+                                               $fields[$name] = $xmls->db->qstr( $field_data );
+                                               $rawfields[$name] = $field_data;
+                               }
+                               
+                               unset($table_fields[$field_id]);
+                               
+                       }
+                       
+                       // check that at least 1 column is specified
+                       if( empty( $fields ) ) {
+                               continue;
+                       }
+                       
+                       // check that no required columns are missing
+                       if( count( $fields ) < $table_field_count ) {
+                               foreach( $table_fields as $field ) {
+                                       if( isset( $field['OPTS'] ) and ( in_array( 'NOTNULL', $field['OPTS'] ) || in_array( 'KEY', $field['OPTS'] ) ) && !in_array( 'AUTOINCREMENT', $field['OPTS'] ) ) {
+                                                       continue(2);
+                                               }
+                               }
+                       }
+                       
+                       // The rest of this method deals with updating existing data records.
+                       
+                       if( !in_array( $table, $tables ) or ( $mode = $xmls->existingData() ) == XMLS_MODE_INSERT ) {
+                               // Table doesn't yet exist, so it's safe to insert.
+                               logMsg( "$table doesn't exist, inserting or mode is INSERT" );
+                       $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')';
+                               continue;
+               }
+               
+                       // Prepare to test for potential violations. Get primary keys and unique indexes
+                       $mfields = array_merge( $fields, $rawfields );
+                       $keyFields = array_intersect( $ukeys, array_keys( $mfields ) );
+                       
+                       if( empty( $ukeys ) or count( $keyFields ) == 0 ) {
+                               // No unique keys in schema, so safe to insert
+                               logMsg( "Either schema or data has no unique keys, so safe to insert" );
+                               $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')';
+                               continue;
+                       }
+                       
+                       // Select record containing matching unique keys.
+                       $where = '';
+                       foreach( $ukeys as $key ) {
+                               if( isset( $mfields[$key] ) and $mfields[$key] ) {
+                                       if( $where ) $where .= ' AND ';
+                                       $where .= $key . ' = ' . $xmls->db->qstr( $mfields[$key] );
+                               }
+                       }
+                       $records = $xmls->db->Execute( 'SELECT * FROM ' . $table . ' WHERE ' . $where );
+                       switch( $records->RecordCount() ) {
+                               case 0:
+                                       // No matching record, so safe to insert.
+                                       logMsg( "No matching records. Inserting new row with unique data" );
+                                       $sql[] = $xmls->db->GetInsertSQL( $records, $mfields );
+                                       break;
+                               case 1:
+                                       // Exactly one matching record, so we can update if the mode permits.
+                                       logMsg( "One matching record..." );
+                                       if( $mode == XMLS_MODE_UPDATE ) {
+                                               logMsg( "...Updating existing row from unique data" );
+                                               $sql[] = $xmls->db->GetUpdateSQL( $records, $mfields );
+                                       }
+                                       break;
+                               default:
+                                       // More than one matching record; the result is ambiguous, so we must ignore the row.
+                                       logMsg( "More than one matching record. Ignoring row." );
+                       }
+               }
+               return $sql;
+       }
+}
+
+/**
+* Creates the SQL to execute a list of provided SQL queries
+*
+* @package axmls
+* @access private
+*/
+class dbQuerySet extends dbObject {
+       
+       /**
+       * @var array    List of SQL queries
+       */
+       var $queries = array();
+       
+       /**
+       * @var string   String used to build of a query line by line
+       */
+       var $query;
+       
+       /**
+       * @var string   Query prefix key
+       */
+       var $prefixKey = '';
+       
+       /**
+       * @var boolean  Auto prefix enable (TRUE)
+       */
+       var $prefixMethod = 'AUTO';
+       
+       /**
+       * Initializes the query set.
+       *
+       * @param object $parent Parent object
+       * @param array $attributes Attributes
+       */
+       function dbQuerySet( &$parent, $attributes = NULL ) {
+               $this->parent = $parent;
+                       
+               // Overrides the manual prefix key
+               if( isset( $attributes['KEY'] ) ) {
+                       $this->prefixKey = $attributes['KEY'];
+               }
+               
+               $prefixMethod = isset( $attributes['PREFIXMETHOD'] ) ? strtoupper( trim( $attributes['PREFIXMETHOD'] ) ) : '';
+               
+               // Enables or disables automatic prefix prepending
+               switch( $prefixMethod ) {
+                       case 'AUTO':
+                               $this->prefixMethod = 'AUTO';
+                               break;
+                       case 'MANUAL':
+                               $this->prefixMethod = 'MANUAL';
+                               break;
+                       case 'NONE':
+                               $this->prefixMethod = 'NONE';
+                               break;
+               }
+       }
+       
+       /**
+       * XML Callback to process start elements. Elements currently 
+       * processed are: QUERY. 
+       *
+       * @access private
+       */
+       function _tag_open( &$parser, $tag, $attributes ) {
+               $this->currentElement = strtoupper( $tag );
+               
+               switch( $this->currentElement ) {
+                       case 'QUERY':
+                               // Create a new query in a SQL queryset.
+                               // Ignore this query set if a platform is specified and it's different than the 
+                               // current connection platform.
+                               if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
+                                       $this->newQuery();
+                               } else {
+                                       $this->discardQuery();
+                               }
+                               break;
+                       default:
+                               // print_r( array( $tag, $attributes ) );
+               }
+       }
+       
+       /**
+       * XML Callback to process CDATA elements
+       */
+       function _tag_cdata( &$parser, $cdata ) {
+               switch( $this->currentElement ) {
+                       // Line of queryset SQL data
+                       case 'QUERY':
+                               $this->buildQuery( $cdata );
+                               break;
+                       default:
+                               
+               }
+       }
+       
+       /**
+       * XML Callback to process end elements
+       *
+       * @access private
+       */
+       function _tag_close( &$parser, $tag ) {
+               $this->currentElement = '';
+               
+               switch( strtoupper( $tag ) ) {
+                       case 'QUERY':
+                               // Add the finished query to the open query set.
+                               $this->addQuery();
+                               break;
+                       case 'SQL':
+                               $this->parent->addSQL( $this->create( $this->parent ) );
+                               xml_set_object( $parser, $this->parent );
+                               $this->destroy();
+                               break;
+                       default:
+                               
+               }
+       }
+       
+       /**
+       * Re-initializes the query.
+       *
+       * @return boolean TRUE
+       */
+       function newQuery() {
+               $this->query = '';
+               
+               return TRUE;
+       }
+       
+       /**
+       * Discards the existing query.
+       *
+       * @return boolean TRUE
+       */
+       function discardQuery() {
+               unset( $this->query );
+               
+               return TRUE;
+       }
+       
+       /** 
+       * Appends a line to a query that is being built line by line
+       *
+       * @param string $data Line of SQL data or NULL to initialize a new query
+       * @return string SQL query string.
+       */
+       function buildQuery( $sql = NULL ) {
+               if( !isset( $this->query ) OR empty( $sql ) ) {
+                       return FALSE;
+               }
+               
+               $this->query .= $sql;
+               
+               return $this->query;
+       }
+       
+       /**
+       * Adds a completed query to the query list
+       *
+       * @return string        SQL of added query
+       */
+       function addQuery() {
+               if( !isset( $this->query ) ) {
+                       return FALSE;
+               }
+               
+               $this->queries[] = $return = trim($this->query);
+               
+               unset( $this->query );
+               
+               return $return;
+       }
+       
+       /**
+       * Creates and returns the current query set
+       *
+       * @param object $xmls adoSchema object
+       * @return array Query set
+       */
+       function create( &$xmls ) {
+               foreach( $this->queries as $id => $query ) {
+                       switch( $this->prefixMethod ) {
+                               case 'AUTO':
+                                       // Enable auto prefix replacement
+                                       
+                                       // Process object prefix.
+                                       // Evaluate SQL statements to prepend prefix to objects
+                                       $query = $this->prefixQuery( '/^\s*((?is)INSERT\s+(INTO\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
+                                       $query = $this->prefixQuery( '/^\s*((?is)UPDATE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
+                                       $query = $this->prefixQuery( '/^\s*((?is)DELETE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
+                                       
+                                       // SELECT statements aren't working yet
+                                       #$data = preg_replace( '/(?ias)(^\s*SELECT\s+.*\s+FROM)\s+(\W\s*,?\s*)+((?i)\s+WHERE.*$)/', "\1 $prefix\2 \3", $data );
+                                       
+                               case 'MANUAL':
+                                       // If prefixKey is set and has a value then we use it to override the default constant XMLS_PREFIX.
+                                       // If prefixKey is not set, we use the default constant XMLS_PREFIX
+                                       if( isset( $this->prefixKey ) AND( $this->prefixKey !== '' ) ) {
+                                               // Enable prefix override
+                                               $query = str_replace( $this->prefixKey, $xmls->objectPrefix, $query );
+                                       } else {
+                                               // Use default replacement
+                                               $query = str_replace( XMLS_PREFIX , $xmls->objectPrefix, $query );
+                                       }
+                       }
+                       
+                       $this->queries[$id] = trim( $query );
+               }
+               
+               // Return the query set array
+               return $this->queries;
+       }
+       
+       /**
+       * Rebuilds the query with the prefix attached to any objects
+       *
+       * @param string $regex Regex used to add prefix
+       * @param string $query SQL query string
+       * @param string $prefix Prefix to be appended to tables, indices, etc.
+       * @return string Prefixed SQL query string.
+       */
+       function prefixQuery( $regex, $query, $prefix = NULL ) {
+               if( !isset( $prefix ) ) {
+                       return $query;
+               }
+               
+               if( preg_match( $regex, $query, $match ) ) {
+                       $preamble = $match[1];
+                       $postamble = $match[5];
+                       $objectList = explode( ',', $match[3] );
+                       // $prefix = $prefix . '_';
+                       
+                       $prefixedList = '';
+                       
+                       foreach( $objectList as $object ) {
+                               if( $prefixedList !== '' ) {
+                                       $prefixedList .= ', ';
+                               }
+                               
+                               $prefixedList .= $prefix . trim( $object );
+                       }
+                       
+                       $query = $preamble . ' ' . $prefixedList . ' ' . $postamble;
+               }
+               
+               return $query;
+       }
+}
+
+/**
+* Loads and parses an XML file, creating an array of "ready-to-run" SQL statements
+* 
+* This class is used to load and parse the XML file, to create an array of SQL statements
+* that can be used to build a database, and to build the database using the SQL array.
+*
+* @tutorial getting_started.pkg
+*
+* @author Richard Tango-Lowy & Dan Cech
+* @version $Revision$
+*
+* @package axmls
+*/
+class adoSchema {
+       
+       /**
+       * @var array    Array containing SQL queries to generate all objects
+       * @access private
+       */
+       var $sqlArray;
+       
+       /**
+       * @var object   ADOdb connection object
+       * @access private
+       */
+       var $db;
+       
+       /**
+       * @var object   ADOdb Data Dictionary
+       * @access private
+       */
+       var $dict;
+       
+       /**
+       * @var string Current XML element
+       * @access private
+       */
+       var $currentElement = '';
+       
+       /**
+       * @var string If set (to 'ALTER' or 'REPLACE'), upgrade an existing database
+       * @access private
+       */
+       var $upgrade = '';
+       
+       /**
+       * @var string Optional object prefix
+       * @access private
+       */
+       var $objectPrefix = '';
+       
+       /**
+       * @var long     Original Magic Quotes Runtime value
+       * @access private
+       */
+       var $mgq;
+       
+       /**
+       * @var long     System debug
+       * @access private
+       */
+       var $debug;
+       
+       /**
+       * @var string Regular expression to find schema version
+       * @access private
+       */
+       var $versionRegex = '/<schema.*?( version="([^"]*)")?.*?>/';
+       
+       /**
+       * @var string Current schema version
+       * @access private
+       */
+       var $schemaVersion;
+       
+       /**
+       * @var int      Success of last Schema execution
+       */
+       var $success;
+       
+       /**
+       * @var bool     Execute SQL inline as it is generated
+       */
+       var $executeInline;
+       
+       /**
+       * @var bool     Continue SQL execution if errors occur
+       */
+       var $continueOnError;
+       
+       /**
+       * @var int      How to handle existing data rows (insert, update, or ignore)
+       */
+       var $existingData;
+       
+       /**
+       * Creates an adoSchema object
+       *
+       * Creating an adoSchema object is the first step in processing an XML schema.
+       * The only parameter is an ADOdb database connection object, which must already
+       * have been created.
+       *
+       * @param object $db ADOdb database connection object.
+       */
+       function adoSchema( &$db ) {
+               // Initialize the environment
+               $this->mgq = get_magic_quotes_runtime();
+               set_magic_quotes_runtime(0);
+               
+               $this->db = $db;
+               $this->debug = $this->db->debug;
+               $this->dict = NewDataDictionary( $this->db );
+               $this->sqlArray = array();
+               $this->schemaVersion = XMLS_SCHEMA_VERSION;
+               $this->executeInline( XMLS_EXECUTE_INLINE );
+               $this->continueOnError( XMLS_CONTINUE_ON_ERROR );
+               $this->existingData( XMLS_EXISTING_DATA );
+               $this->setUpgradeMethod();
+       }
+       
+       /**
+       * Sets the method to be used for upgrading an existing database
+       *
+       * Use this method to specify how existing database objects should be upgraded.
+       * The method option can be set to ALTER, REPLACE, BEST, or NONE. ALTER attempts to
+       * alter each database object directly, REPLACE attempts to rebuild each object
+       * from scratch, BEST attempts to determine the best upgrade method for each
+       * object, and NONE disables upgrading.
+       *
+       * This method is not yet used by AXMLS, but exists for backward compatibility.
+       * The ALTER method is automatically assumed when the adoSchema object is
+       * instantiated; other upgrade methods are not currently supported.
+       *
+       * @param string $method Upgrade method (ALTER|REPLACE|BEST|NONE)
+       * @returns string Upgrade method used
+       */
+       function SetUpgradeMethod( $method = '' ) {
+               if( !is_string( $method ) ) {
+                       return FALSE;
+               }
+               
+               $method = strtoupper( $method );
+               
+               // Handle the upgrade methods
+               switch( $method ) {
+                       case 'ALTER':
+                               $this->upgrade = $method;
+                               break;
+                       case 'REPLACE':
+                               $this->upgrade = $method;
+                               break;
+                       case 'BEST':
+                               $this->upgrade = 'ALTER';
+                               break;
+                       case 'NONE':
+                               $this->upgrade = 'NONE';
+                               break;
+                       default:
+                               // Use default if no legitimate method is passed.
+                               $this->upgrade = XMLS_DEFAULT_UPGRADE_METHOD;
+               }
+               
+               return $this->upgrade;
+       }
+       
+       /**
+       * Specifies how to handle existing data row when there is a unique key conflict.
+       *
+       * The existingData setting specifies how the parser should handle existing rows
+       * when a unique key violation occurs during the insert. This can happen when inserting
+       * data into an existing table with one or more primary keys or unique indexes.
+       * The existingData method takes one of three options: XMLS_MODE_INSERT attempts
+       * to always insert the data as a new row. In the event of a unique key violation,
+       * the database will generate an error.  XMLS_MODE_UPDATE attempts to update the 
+       * any existing rows with the new data based upon primary or unique key fields in
+       * the schema. If the data row in the schema specifies no unique fields, the row
+       * data will be inserted as a new row. XMLS_MODE_IGNORE specifies that any data rows
+       * that would result in a unique key violation be ignored; no inserts or updates will
+       * take place. For backward compatibility, the default setting is XMLS_MODE_INSERT,
+       * but XMLS_MODE_UPDATE will generally be the most appropriate setting.
+       *
+       * @param int $mode XMLS_MODE_INSERT, XMLS_MODE_UPDATE, or XMLS_MODE_IGNORE
+       * @return int current mode
+       */
+       function ExistingData( $mode = NULL ) {
+               if( is_int( $mode ) ) {
+                       switch( $mode ) {
+                               case XMLS_MODE_UPDATE:
+                                       $mode = XMLS_MODE_UPDATE;
+                                       break;
+                               case XMLS_MODE_IGNORE:
+                                       $mode = XMLS_MODE_IGNORE;
+                                       break;
+                               case XMLS_MODE_INSERT:
+                                       $mode = XMLS_MODE_INSERT;
+                                       break;
+                               default:
+                                       $mode = XMLS_EXISTING_DATA;
+                                       break;
+                       }
+                       $this->existingData = $mode;
+               }
+               
+               return $this->existingData;
+       }
+       
+       /**
+       * Enables/disables inline SQL execution.
+       *
+       * Call this method to enable or disable inline execution of the schema. If the mode is set to TRUE (inline execution),
+       * AXMLS applies the SQL to the database immediately as each schema entity is parsed. If the mode
+       * is set to FALSE (post execution), AXMLS parses the entire schema and you will need to call adoSchema::ExecuteSchema()
+       * to apply the schema to the database.
+       *
+       * @param bool $mode execute
+       * @return bool current execution mode
+       *
+       * @see ParseSchema(), ExecuteSchema()
+       */
+       function ExecuteInline( $mode = NULL ) {
+               if( is_bool( $mode ) ) {
+                       $this->executeInline = $mode;
+               }
+               
+               return $this->executeInline;
+       }
+       
+       /**
+       * Enables/disables SQL continue on error.
+       *
+       * Call this method to enable or disable continuation of SQL execution if an error occurs.
+       * If the mode is set to TRUE (continue), AXMLS will continue to apply SQL to the database, even if an error occurs.
+       * If the mode is set to FALSE (halt), AXMLS will halt execution of generated sql if an error occurs, though parsing
+       * of the schema will continue.
+       *
+       * @param bool $mode execute
+       * @return bool current continueOnError mode
+       *
+       * @see addSQL(), ExecuteSchema()
+       */
+       function ContinueOnError( $mode = NULL ) {
+               if( is_bool( $mode ) ) {
+                       $this->continueOnError = $mode;
+               }
+               
+               return $this->continueOnError;
+       }
+       
+       /**
+       * Loads an XML schema from a file and converts it to SQL.
+       *
+       * Call this method to load the specified schema (see the DTD for the proper format) from
+       * the filesystem and generate the SQL necessary to create the database
+       * described. This method automatically converts the schema to the latest
+       * axmls schema version.
+       * @see ParseSchemaString()
+       *
+       * @param string $file Name of XML schema file.
+       * @param bool $returnSchema Return schema rather than parsing.
+       * @return array Array of SQL queries, ready to execute
+       */
+       function ParseSchema( $filename, $returnSchema = FALSE ) {
+               return $this->ParseSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );
+       }
+       
+       /**
+       * Loads an XML schema from a file and converts it to SQL.
+       *
+       * Call this method to load the specified schema directly from a file (see
+       * the DTD for the proper format) and generate the SQL necessary to create
+       * the database described by the schema. Use this method when you are dealing
+       * with large schema files. Otherwise, ParseSchema() is faster.
+       * This method does not automatically convert the schema to the latest axmls
+       * schema version. You must convert the schema manually using either the
+       * ConvertSchemaFile() or ConvertSchemaString() method.
+       * @see ParseSchema()
+       * @see ConvertSchemaFile()
+       * @see ConvertSchemaString()
+       *
+       * @param string $file Name of XML schema file.
+       * @param bool $returnSchema Return schema rather than parsing.
+       * @return array Array of SQL queries, ready to execute.
+       *
+       * @deprecated Replaced by adoSchema::ParseSchema() and adoSchema::ParseSchemaString()
+       * @see ParseSchema(), ParseSchemaString()
+       */
+       function ParseSchemaFile( $filename, $returnSchema = FALSE ) {
+               // Open the file
+               if( !($fp = fopen( $filename, 'r' )) ) {
+                       logMsg( 'Unable to open file' );
+                       return FALSE;
+               }
+               
+               // do version detection here
+               if( $this->SchemaFileVersion( $filename ) != $this->schemaVersion ) {
+                       logMsg( 'Invalid Schema Version' );
+                       return FALSE;
+               }
+               
+               if( $returnSchema ) {
+                       $xmlstring = '';
+                       while( $data = fread( $fp, 4096 ) ) {
+                               $xmlstring .= $data . "\n";
+                       }
+                       return $xmlstring;
+               }
+               
+               $this->success = 2;
+               
+               $xmlParser = $this->create_parser();
+               
+               // Process the file
+               while( $data = fread( $fp, 4096 ) ) {
+                       if( !xml_parse( $xmlParser, $data, feof( $fp ) ) ) {
+                               die( sprintf(
+                                       "XML error: %s at line %d",
+                                       xml_error_string( xml_get_error_code( $xmlParser) ),
+                                       xml_get_current_line_number( $xmlParser)
+                               ) );
+                       }
+               }
+               
+               xml_parser_free( $xmlParser );
+               
+               return $this->sqlArray;
+       }
+       
+       /**
+       * Converts an XML schema string to SQL.
+       *
+       * Call this method to parse a string containing an XML schema (see the DTD for the proper format)
+       * and generate the SQL necessary to create the database described by the schema. 
+       * @see ParseSchema()
+       *
+       * @param string $xmlstring XML schema string.
+       * @param bool $returnSchema Return schema rather than parsing.
+       * @return array Array of SQL queries, ready to execute.
+       */
+       function ParseSchemaString( $xmlstring, $returnSchema = FALSE ) {
+               if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {
+                       logMsg( 'Empty or Invalid Schema' );
+                       return FALSE;
+               }
+               
+               // do version detection here
+               if( $this->SchemaStringVersion( $xmlstring ) != $this->schemaVersion ) {
+                       logMsg( 'Invalid Schema Version' );
+                       return FALSE;
+               }
+               
+               if( $returnSchema ) {
+                       return $xmlstring;
+               }
+               
+               $this->success = 2;
+               
+               $xmlParser = $this->create_parser();
+               
+               if( !xml_parse( $xmlParser, $xmlstring, TRUE ) ) {
+                       die( sprintf(
+                               "XML error: %s at line %d",
+                               xml_error_string( xml_get_error_code( $xmlParser) ),
+                               xml_get_current_line_number( $xmlParser)
+                       ) );
+               }
+               
+               xml_parser_free( $xmlParser );
+               
+               return $this->sqlArray;
+       }
+       
+       /**
+       * Loads an XML schema from a file and converts it to uninstallation SQL.
+       *
+       * Call this method to load the specified schema (see the DTD for the proper format) from
+       * the filesystem and generate the SQL necessary to remove the database described.
+       * @see RemoveSchemaString()
+       *
+       * @param string $file Name of XML schema file.
+       * @param bool $returnSchema Return schema rather than parsing.
+       * @return array Array of SQL queries, ready to execute
+       */
+       function RemoveSchema( $filename, $returnSchema = FALSE ) {
+               return $this->RemoveSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );
+       }
+       
+       /**
+       * Converts an XML schema string to uninstallation SQL.
+       *
+       * Call this method to parse a string containing an XML schema (see the DTD for the proper format)
+       * and generate the SQL necessary to uninstall the database described by the schema. 
+       * @see RemoveSchema()
+       *
+       * @param string $schema XML schema string.
+       * @param bool $returnSchema Return schema rather than parsing.
+       * @return array Array of SQL queries, ready to execute.
+       */
+       function RemoveSchemaString( $schema, $returnSchema = FALSE ) {
+               
+               // grab current version
+               if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {
+                       return FALSE;
+               }
+               
+               return $this->ParseSchemaString( $this->TransformSchema( $schema, 'remove-' . $version), $returnSchema );
+       }
+       
+       /**
+       * Applies the current XML schema to the database (post execution).
+       *
+       * Call this method to apply the current schema (generally created by calling 
+       * ParseSchema() or ParseSchemaString() ) to the database (creating the tables, indexes, 
+       * and executing other SQL specified in the schema) after parsing.
+       * @see ParseSchema(), ParseSchemaString(), ExecuteInline()
+       *
+       * @param array $sqlArray Array of SQL statements that will be applied rather than
+       *               the current schema.
+       * @param boolean $continueOnErr Continue to apply the schema even if an error occurs.
+       * @returns integer 0 if failure, 1 if errors, 2 if successful.
+       */
+       function ExecuteSchema( $sqlArray = NULL, $continueOnErr =  NULL ) {
+               if( !is_bool( $continueOnErr ) ) {
+                       $continueOnErr = $this->ContinueOnError();
+               }
+               
+               if( !isset( $sqlArray ) ) {
+                       $sqlArray = $this->sqlArray;
+               }
+               
+               if( !is_array( $sqlArray ) ) {
+                       $this->success = 0;
+               } else {
+                       $this->success = $this->dict->ExecuteSQLArray( $sqlArray, $continueOnErr );
+               }
+               
+               return $this->success;
+       }
+       
+       /**
+       * Returns the current SQL array. 
+       *
+       * Call this method to fetch the array of SQL queries resulting from 
+       * ParseSchema() or ParseSchemaString(). 
+       *
+       * @param string $format Format: HTML, TEXT, or NONE (PHP array)
+       * @return array Array of SQL statements or FALSE if an error occurs
+       */
+       function PrintSQL( $format = 'NONE' ) {
+               $sqlArray = null;
+               return $this->getSQL( $format, $sqlArray );
+       }
+       
+       /**
+       * Saves the current SQL array to the local filesystem as a list of SQL queries.
+       *
+       * Call this method to save the array of SQL queries (generally resulting from a
+       * parsed XML schema) to the filesystem.
+       *
+       * @param string $filename Path and name where the file should be saved.
+       * @return boolean TRUE if save is successful, else FALSE. 
+       */
+       function SaveSQL( $filename = './schema.sql' ) {
+               
+               if( !isset( $sqlArray ) ) {
+                       $sqlArray = $this->sqlArray;
+               }
+               if( !isset( $sqlArray ) ) {
+                       return FALSE;
+               }
+               
+               $fp = fopen( $filename, "w" );
+               
+               foreach( $sqlArray as $key => $query ) {
+                       fwrite( $fp, $query . ";\n" );
+               }
+               fclose( $fp );
+       }
+       
+       /**
+       * Create an xml parser
+       *
+       * @return object PHP XML parser object
+       *
+       * @access private
+       */
+       function create_parser() {
+               // Create the parser
+               $xmlParser = xml_parser_create();
+               xml_set_object( $xmlParser, $this );
+               
+               // Initialize the XML callback functions
+               xml_set_element_handler( $xmlParser, '_tag_open', '_tag_close' );
+               xml_set_character_data_handler( $xmlParser, '_tag_cdata' );
+               
+               return $xmlParser;
+       }
+       
+       /**
+       * XML Callback to process start elements
+       *
+       * @access private
+       */
+       function _tag_open( &$parser, $tag, $attributes ) {
+               switch( strtoupper( $tag ) ) {
+                       case 'TABLE':
+                               if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
+                               $this->obj = new dbTable( $this, $attributes );
+                               xml_set_object( $parser, $this->obj );
+                               }
+                               break;
+                       case 'SQL':
+                               if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
+                                       $this->obj = new dbQuerySet( $this, $attributes );
+                                       xml_set_object( $parser, $this->obj );
+                               }
+                               break;
+                       default:
+                               // print_r( array( $tag, $attributes ) );
+               }
+               
+       }
+       
+       /**
+       * XML Callback to process CDATA elements
+       *
+       * @access private
+       */
+       function _tag_cdata( &$parser, $cdata ) {
+       }
+       
+       /**
+       * XML Callback to process end elements
+       *
+       * @access private
+       * @internal
+       */
+       function _tag_close( &$parser, $tag ) {
+               
+       }
+       
+       /**
+       * Converts an XML schema string to the specified DTD version.
+       *
+       * Call this method to convert a string containing an XML schema to a different AXMLS
+       * DTD version. For instance, to convert a schema created for an pre-1.0 version for 
+       * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version 
+       * parameter is specified, the schema will be converted to the current DTD version. 
+       * If the newFile parameter is provided, the converted schema will be written to the specified
+       * file.
+       * @see ConvertSchemaFile()
+       *
+       * @param string $schema String containing XML schema that will be converted.
+       * @param string $newVersion DTD version to convert to.
+       * @param string $newFile File name of (converted) output file.
+       * @return string Converted XML schema or FALSE if an error occurs.
+       */
+       function ConvertSchemaString( $schema, $newVersion = NULL, $newFile = NULL ) {
+               
+               // grab current version
+               if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {
+                       return FALSE;
+               }
+               
+               if( !isset ($newVersion) ) {
+                       $newVersion = $this->schemaVersion;
+               }
+               
+               if( $version == $newVersion ) {
+                       $result = $schema;
+               } else {
+                       $result = $this->TransformSchema( $schema, 'convert-' . $version . '-' . $newVersion);
+               }
+               
+               if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {
+                       fwrite( $fp, $result );
+                       fclose( $fp );
+               }
+               
+               return $result;
+       }
+
+       /*
+       // compat for pre-4.3 - jlim
+       function _file_get_contents($path)
+       {
+               if (function_exists('file_get_contents')) return file_get_contents($path);
+               return join('',file($path));
+       }*/
+       
+       /**
+       * Converts an XML schema file to the specified DTD version.
+       *
+       * Call this method to convert the specified XML schema file to a different AXMLS
+       * DTD version. For instance, to convert a schema created for an pre-1.0 version for 
+       * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version 
+       * parameter is specified, the schema will be converted to the current DTD version. 
+       * If the newFile parameter is provided, the converted schema will be written to the specified
+       * file.
+       * @see ConvertSchemaString()
+       *
+       * @param string $filename Name of XML schema file that will be converted.
+       * @param string $newVersion DTD version to convert to.
+       * @param string $newFile File name of (converted) output file.
+       * @return string Converted XML schema or FALSE if an error occurs.
+       */
+       function ConvertSchemaFile( $filename, $newVersion = NULL, $newFile = NULL ) {
+               
+               // grab current version
+               if( !( $version = $this->SchemaFileVersion( $filename ) ) ) {
+                       return FALSE;
+               }
+               
+               if( !isset ($newVersion) ) {
+                       $newVersion = $this->schemaVersion;
+               }
+               
+               if( $version == $newVersion ) {
+                       $result = _file_get_contents( $filename );
+                       
+                       // remove unicode BOM if present
+                       if( substr( $result, 0, 3 ) == sprintf( '%c%c%c', 239, 187, 191 ) ) {
+                               $result = substr( $result, 3 );
+                       }
+               } else {
+                       $result = $this->TransformSchema( $filename, 'convert-' . $version . '-' . $newVersion, 'file' );
+               }
+               
+               if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {
+                       fwrite( $fp, $result );
+                       fclose( $fp );
+               }
+               
+               return $result;
+       }
+       
+       function TransformSchema( $schema, $xsl, $schematype='string' )
+       {
+               // Fail if XSLT extension is not available
+               if( ! function_exists( 'xslt_create' ) ) {
+                       return FALSE;
+               }
+               
+               $xsl_file = dirname( __FILE__ ) . '/xsl/' . $xsl . '.xsl';
+               
+               // look for xsl
+               if( !is_readable( $xsl_file ) ) {
+                       return FALSE;
+               }
+               
+               switch( $schematype )
+               {
+                       case 'file':
+                               if( !is_readable( $schema ) ) {
+                                       return FALSE;
+                               }
+                               
+                               $schema = _file_get_contents( $schema );
+                               break;
+                       case 'string':
+                       default:
+                               if( !is_string( $schema ) ) {
+                                       return FALSE;
+                               }
+               }
+               
+               $arguments = array (
+                       '/_xml' => $schema,
+                       '/_xsl' => _file_get_contents( $xsl_file )
+               );
+               
+               // create an XSLT processor
+               $xh = xslt_create ();
+               
+               // set error handler
+               xslt_set_error_handler ($xh, array (&$this, 'xslt_error_handler'));
+               
+               // process the schema
+               $result = xslt_process ($xh, 'arg:/_xml', 'arg:/_xsl', NULL, $arguments); 
+               
+               xslt_free ($xh);
+               
+               return $result;
+       }
+       
+       /**
+       * Processes XSLT transformation errors
+       *
+       * @param object $parser XML parser object
+       * @param integer $errno Error number
+       * @param integer $level Error level
+       * @param array $fields Error information fields
+       *
+       * @access private
+       */
+       function xslt_error_handler( $parser, $errno, $level, $fields ) {
+               if( is_array( $fields ) ) {
+                       $msg = array(
+                               'Message Type' => ucfirst( $fields['msgtype'] ),
+                               'Message Code' => $fields['code'],
+                               'Message' => $fields['msg'],
+                               'Error Number' => $errno,
+                               'Level' => $level
+                       );
+                       
+                       switch( $fields['URI'] ) {
+                               case 'arg:/_xml':
+                                       $msg['Input'] = 'XML';
+                                       break;
+                               case 'arg:/_xsl':
+                                       $msg['Input'] = 'XSL';
+                                       break;
+                               default:
+                                       $msg['Input'] = $fields['URI'];
+                       }
+                       
+                       $msg['Line'] = $fields['line'];
+               } else {
+                       $msg = array(
+                               'Message Type' => 'Error',
+                               'Error Number' => $errno,
+                               'Level' => $level,
+                               'Fields' => var_export( $fields, TRUE )
+                       );
+               }
+               
+               $error_details = $msg['Message Type'] . ' in XSLT Transformation' . "\n"
+                                          . '<table>' . "\n";
+               
+               foreach( $msg as $label => $details ) {
+                       $error_details .= '<tr><td><b>' . $label . ': </b></td><td>' . htmlentities( $details ) . '</td></tr>' . "\n";
+               }
+               
+               $error_details .= '</table>';
+               
+               trigger_error( $error_details, E_USER_ERROR );
+       }
+       
+       /**
+       * Returns the AXMLS Schema Version of the requested XML schema file.
+       *
+       * Call this method to obtain the AXMLS DTD version of the requested XML schema file.
+       * @see SchemaStringVersion()
+       *
+       * @param string $filename AXMLS schema file
+       * @return string Schema version number or FALSE on error
+       */
+       function SchemaFileVersion( $filename ) {
+               // Open the file
+               if( !($fp = fopen( $filename, 'r' )) ) {
+                       // die( 'Unable to open file' );
+                       return FALSE;
+               }
+               
+               // Process the file
+               while( $data = fread( $fp, 4096 ) ) {
+                       if( preg_match( $this->versionRegex, $data, $matches ) ) {
+                               return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;
+                       }
+               }
+               
+               return FALSE;
+       }
+       
+       /**
+       * Returns the AXMLS Schema Version of the provided XML schema string.
+       *
+       * Call this method to obtain the AXMLS DTD version of the provided XML schema string.
+       * @see SchemaFileVersion()
+       *
+       * @param string $xmlstring XML schema string
+       * @return string Schema version number or FALSE on error
+       */
+       function SchemaStringVersion( $xmlstring ) {
+               if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {
+                       return FALSE;
+               }
+               
+               if( preg_match( $this->versionRegex, $xmlstring, $matches ) ) {
+                       return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;
+               }
+               
+               return FALSE;
+       }
+       
+       /**
+       * Extracts an XML schema from an existing database.
+       *
+       * Call this method to create an XML schema string from an existing database.
+       * If the data parameter is set to TRUE, AXMLS will include the data from the database
+       * in the schema. 
+       *
+       * @param boolean $data Include data in schema dump
+       * @indent string indentation to use
+       * @prefix string extract only tables with given prefix
+       * @stripprefix strip prefix string when storing in XML schema
+       * @return string Generated XML schema
+       */
+       function ExtractSchema( $data = FALSE, $indent = '  ', $prefix = '' , $stripprefix=false) {
+               $old_mode = $this->db->SetFetchMode( ADODB_FETCH_NUM );
+               
+               $schema = '<?xml version="1.0"?>' . "\n"
+                               . '<schema version="' . $this->schemaVersion . '">' . "\n";
+               
+               if( is_array( $tables = $this->db->MetaTables( 'TABLES' , ($prefix) ? $prefix.'%' : '') ) ) {
+                       foreach( $tables as $table ) {
+                               if ($stripprefix) $table = str_replace(str_replace('\\_', '_', $pfx ), '', $table);
+                               $schema .= $indent . '<table name="' . htmlentities( $table ) . '">' . "\n";
+                               
+                               // grab details from database
+                               $rs = $this->db->Execute( 'SELECT * FROM ' . $table . ' WHERE -1' );
+                               $fields = $this->db->MetaColumns( $table );
+                               $indexes = $this->db->MetaIndexes( $table );
+                               
+                               if( is_array( $fields ) ) {
+                                       foreach( $fields as $details ) {
+                                               $extra = '';
+                                               $content = array();
+                                               
+                                               if( isset($details->max_length) && $details->max_length > 0 ) {
+                                                       $extra .= ' size="' . $details->max_length . '"';
+                                               }
+                                               
+                                               if( isset($details->primary_key) && $details->primary_key ) {
+                                                       $content[] = '<KEY/>';
+                                               } elseif( isset($details->not_null) && $details->not_null ) {
+                                                       $content[] = '<NOTNULL/>';
+                                               }
+                                               
+                                               if( isset($details->has_default) && $details->has_default ) {
+                                                       $content[] = '<DEFAULT value="' . htmlentities( $details->default_value ) . '"/>';
+                                               }
+                                               
+                                               if( isset($details->auto_increment) && $details->auto_increment ) {
+                                                       $content[] = '<AUTOINCREMENT/>';
+                                               }
+                                               
+                                               if( isset($details->unsigned) && $details->unsigned ) {
+                                                       $content[] = '<UNSIGNED/>';
+                                               }
+                                               
+                                               // this stops the creation of 'R' columns,
+                                               // AUTOINCREMENT is used to create auto columns
+                                               $details->primary_key = 0;
+                                               $type = $rs->MetaType( $details );
+                                               
+                                               $schema .= str_repeat( $indent, 2 ) . '<field name="' . htmlentities( $details->name ) . '" type="' . $type . '"' . $extra;
+                                               
+                                               if( !empty( $content ) ) {
+                                                       $schema .= ">\n" . str_repeat( $indent, 3 )
+                                                                        . implode( "\n" . str_repeat( $indent, 3 ), $content ) . "\n"
+                                                                        . str_repeat( $indent, 2 ) . '</field>' . "\n";
+                                               } else {
+                                                       $schema .= "/>\n";
+                                               }
+                                       }
+                               }
+                               
+                               if( is_array( $indexes ) ) {
+                                       foreach( $indexes as $index => $details ) {
+                                               $schema .= str_repeat( $indent, 2 ) . '<index name="' . $index . '">' . "\n";
+                                               
+                                               if( $details['unique'] ) {
+                                                       $schema .= str_repeat( $indent, 3 ) . '<UNIQUE/>' . "\n";
+                                               }
+                                               
+                                               foreach( $details['columns'] as $column ) {
+                                                       $schema .= str_repeat( $indent, 3 ) . '<col>' . htmlentities( $column ) . '</col>' . "\n";
+                                               }
+                                               
+                                               $schema .= str_repeat( $indent, 2 ) . '</index>' . "\n";
+                                       }
+                               }
+                               
+                               if( $data ) {
+                                       $rs = $this->db->Execute( 'SELECT * FROM ' . $table );
+                                       
+                                       if( is_object( $rs ) && !$rs->EOF ) {
+                                               $schema .= str_repeat( $indent, 2 ) . "<data>\n";
+                                               
+                                               while( $row = $rs->FetchRow() ) {
+                                                       foreach( $row as $key => $val ) {
+                                                               if ( $val != htmlentities( $val ) ) {
+                                                                       $row[$key] = '<![CDATA[' . $val . ']]>';
+                                                               }
+                                                       }
+                                                       
+                                                       $schema .= str_repeat( $indent, 3 ) . '<row><f>' . implode( '</f><f>', $row ) . "</f></row>\n";
+                                               }
+                                               
+                                               $schema .= str_repeat( $indent, 2 ) . "</data>\n";
+                                       }
+                               }
+                               
+                               $schema .= $indent . "</table>\n";
+                       }
+               }
+               
+               $this->db->SetFetchMode( $old_mode );
+               
+               $schema .= '</schema>';
+               return $schema;
+       }
+       
+       /**
+       * Sets a prefix for database objects
+       *
+       * Call this method to set a standard prefix that will be prepended to all database tables 
+       * and indices when the schema is parsed. Calling setPrefix with no arguments clears the prefix.
+       *
+       * @param string $prefix Prefix that will be prepended.
+       * @param boolean $underscore If TRUE, automatically append an underscore character to the prefix.
+       * @return boolean TRUE if successful, else FALSE
+       */
+       function SetPrefix( $prefix = '', $underscore = TRUE ) {
+               switch( TRUE ) {
+                       // clear prefix
+                       case empty( $prefix ):
+                               logMsg( 'Cleared prefix' );
+                               $this->objectPrefix = '';
+                               return TRUE;
+                       // prefix too long
+                       case strlen( $prefix ) > XMLS_PREFIX_MAXLEN:
+                       // prefix contains invalid characters
+                       case !preg_match( '/^[a-z][a-z0-9_]+$/i', $prefix ):
+                               logMsg( 'Invalid prefix: ' . $prefix );
+                               return FALSE;
+               }
+               
+               if( $underscore AND substr( $prefix, -1 ) != '_' ) {
+                       $prefix .= '_';
+               }
+               
+               // prefix valid
+               logMsg( 'Set prefix: ' . $prefix );
+               $this->objectPrefix = $prefix;
+               return TRUE;
+       }
+       
+       /**
+       * Returns an object name with the current prefix prepended.
+       *
+       * @param string $name Name
+       * @return string        Prefixed name
+       *
+       * @access private
+       */
+       function prefix( $name = '' ) {
+               // if prefix is set
+               if( !empty( $this->objectPrefix ) ) {
+                       // Prepend the object prefix to the table name
+                       // prepend after quote if used
+                       return preg_replace( '/^(`?)(.+)$/', '$1' . $this->objectPrefix . '$2', $name );
+               }
+               
+               // No prefix set. Use name provided.
+               return $name;
+       }
+       
+       /**
+       * Checks if element references a specific platform
+       *
+       * @param string $platform Requested platform
+       * @returns boolean TRUE if platform check succeeds
+       *
+       * @access private
+       */
+       function supportedPlatform( $platform = NULL ) {
+               if( !empty( $platform ) ) {
+                       $regex = '/(^|\|)' . $this->db->databaseType . '(\||$)/i';
+               
+                       if( preg_match( '/^- /', $platform ) ) {
+                               if (preg_match ( $regex, substr( $platform, 2 ) ) ) {
+                                       logMsg( 'Platform ' . $platform . ' is NOT supported' );
+                                       return FALSE;
+                               }
+               } else {
+                               if( !preg_match ( $regex, $platform ) ) {
+                                       logMsg( 'Platform ' . $platform . ' is NOT supported' );
+                       return FALSE;
+               }
+       }
+               }
+               
+               logMsg( 'Platform ' . $platform . ' is supported' );
+               return TRUE;
+       }
+       
+       /**
+       * Clears the array of generated SQL.
+       *
+       * @access private
+       */
+       function clearSQL() {
+               $this->sqlArray = array();
+       }
+       
+       /**
+       * Adds SQL into the SQL array.
+       *
+       * @param mixed $sql SQL to Add
+       * @return boolean TRUE if successful, else FALSE.
+       *
+       * @access private
+       */      
+       function addSQL( $sql = NULL ) {
+               if( is_array( $sql ) ) {
+                       foreach( $sql as $line ) {
+                               $this->addSQL( $line );
+                       }
+                       
+                       return TRUE;
+               }
+               
+               if( is_string( $sql ) ) {
+                       $this->sqlArray[] = $sql;
+                       
+                       // if executeInline is enabled, and either no errors have occurred or continueOnError is enabled, execute SQL.
+                       if( $this->ExecuteInline() && ( $this->success == 2 || $this->ContinueOnError() ) ) {
+                               $saved = $this->db->debug;
+                               $this->db->debug = $this->debug;
+                               $ok = $this->db->Execute( $sql );
+                               $this->db->debug = $saved;
+                               
+                               if( !$ok ) {
+                                       if( $this->debug ) {
+                                               ADOConnection::outp( $this->db->ErrorMsg() );
+                                       }
+                                       
+                                       $this->success = 1;
+                               }
+                       }
+                       
+                       return TRUE;
+               }
+               
+               return FALSE;
+       }
+       
+       /**
+       * Gets the SQL array in the specified format.
+       *
+       * @param string $format Format
+       * @return mixed SQL
+       *       
+       * @access private
+       */
+       function getSQL( $format = NULL, $sqlArray = NULL ) {
+               if( !is_array( $sqlArray ) ) {
+                       $sqlArray = $this->sqlArray;
+               }
+               
+               if( !is_array( $sqlArray ) ) {
+                       return FALSE;
+               }
+               
+               switch( strtolower( $format ) ) {
+                       case 'string':
+                       case 'text':
+                               return !empty( $sqlArray ) ? implode( ";\n\n", $sqlArray ) . ';' : '';
+                       case'html':
+                               return !empty( $sqlArray ) ? nl2br( htmlentities( implode( ";\n\n", $sqlArray ) . ';' ) ) : '';
+               }
+               
+               return $this->sqlArray;
+       }
+       
+       /**
+       * Destroys an adoSchema object.
+       *
+       * Call this method to clean up after an adoSchema object that is no longer in use.
+       * @deprecated adoSchema now cleans up automatically.
+       */
+       function Destroy() {
+               set_magic_quotes_runtime( $this->mgq );
+               unset( $this );
+       }
+}
+
+/**
+* Message logging function
+*
+* @access private
+*/
+function logMsg( $msg, $title = NULL, $force = FALSE ) {
+       if( XMLS_DEBUG or $force ) {
+               echo '<pre>';
+               
+               if( isset( $title ) ) {
+                       echo '<h3>' . htmlentities( $title ) . '</h3>';
+               }
+               
+               if( @is_object( $this ) ) {
+                       echo '[' . get_class( $this ) . '] ';
+               }
+               
+               print_r( $msg );
+               
+               echo '</pre>';
+       }
+}
 ?>
\ No newline at end of file
index 42fc35a2e90c61e4bb33897a1932771d7fa47800..9a619ffb5b5b24b6855ea76c3e187d465b473da2 100644 (file)
@@ -12,9 +12,9 @@
  */
 
 /**
-       \mainpage       
+       \mainpage
        
-        @version V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+        @version V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). All rights reserved.
 
        Released under both BSD license and Lesser GPL library license. You can choose which license
        you prefer.
                $ADODB_vers,            // database version
                $ADODB_COUNTRECS,       // count number of records returned - slows down query
                $ADODB_CACHE_DIR,       // directory to cache recordsets
+               $ADODB_CACHE,
+               $ADODB_CACHE_CLASS,
                $ADODB_EXTENSION,   // ADODB extension installed
                $ADODB_COMPAT_FETCH, // If $ADODB_COUNTRECS and this is true, $rs->fields is available on EOF
                $ADODB_FETCH_MODE,      // DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default...
+               $ADODB_GETONE_EOF,
                $ADODB_QUOTE_FIELDNAMES; // Allows you to force quotes (backticks) around field names in queries generated by getinsertsql and getupdatesql.    
        
        //==============================================================================================        
                        die("PHP5 or later required. You are running ".PHP_VERSION);
        }
        
+       
        //if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2);
 
        
                $ADODB_COUNTRECS,       // count number of records returned - slows down query
                $ADODB_CACHE_DIR,       // directory to cache recordsets
                $ADODB_FETCH_MODE,
+               $ADODB_CACHE,
+               $ADODB_CACHE_CLASS,
                $ADODB_FORCE_TYPE,
+               $ADODB_GETONE_EOF,
                $ADODB_QUOTE_FIELDNAMES;
                
+               if (empty($ADODB_CACHE_CLASS)) $ADODB_CACHE_CLASS =  'ADODB_Cache_File' ;
                $ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT;
                $ADODB_FORCE_TYPE = ADODB_FORCE_VALUE;
-
+               $ADODB_GETONE_EOF = null;
 
                if (!isset($ADODB_CACHE_DIR)) {
                        $ADODB_CACHE_DIR = '/tmp'; //(isset($_ENV['TMP'])) ? $_ENV['TMP'] : '/tmp';
                /**
                 * ADODB version as a string.
                 */
-               $ADODB_vers = 'V5.04a 25 Mar 2008  (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved. Released BSD & LGPL.';
+               $ADODB_vers = 'V5.08 6 Apr 2009  (c) 2000-2009 John Lim (jlim#natsoft.com). All rights reserved. Released BSD & LGPL.';
        
                /**
                 * Determines whether recordset->RecordCount() is used. 
 */
        }
        
-
+       // for transaction handling
        
        function ADODB_TransMonitor($dbms, $fn, $errno, $errmsg, $p1, $p2, &$thisConnection)
        {
                }
        }
        
+       //------------------
+       // class for caching
+       class ADODB_Cache_File {
+       
+               var $createdir = true; // requires creation of temp dirs
+               
+               function ADODB_Cache_File()
+               {
+               global $ADODB_INCLUDED_CSV;
+                       if (empty($ADODB_INCLUDED_CSV)) include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
+               }
+               
+               // write serialised recordset to cache item/file
+               function writecache($filename, $contents,  $debug, $secs2cache)
+               {
+                       return adodb_write_file($filename, $contents,$debug);
+               }
+               
+               // load serialised recordset and unserialise it
+               function &readcache($filename, &$err, $secs2cache, $rsClass)
+               {
+                       $rs = csv2rs($filename,$err,$secs2cache,$rsClass);
+                       return $rs;
+               }
+               
+               // flush all items in cache
+               function flushall($debug=false)
+               {
+               global $ADODB_CACHE_DIR;
+
+               $rez = false;
+               
+                       if (strlen($ADODB_CACHE_DIR) > 1) {
+                               $rez = $this->_dirFlush($ADODB_CACHE_DIR);
+                       if ($debug) ADOConnection::outp( "flushall: $dir<br><pre>\n". $rez."</pre>");
+                       }
+                       return $rez;
+               }
+               
+               // flush one file in cache
+               function flushcache($f, $debug=false)
+               {
+                       if (!@unlink($f)) {
+                               if ($debug) ADOConnection::outp( "flushcache: failed for $f");
+                       }
+               }
+               
+               function getdirname($hash)
+               {
+               global $ADODB_CACHE_DIR;
+                       if (!isset($this->notSafeMode)) $this->notSafeMode = !ini_get('safe_mode');
+                       return ($this->notSafeMode) ? $ADODB_CACHE_DIR.'/'.substr($hash,0,2) : $ADODB_CACHE_DIR;
+               }
+               
+               // create temp directories
+               function createdir($hash, $debug)
+               {
+                       $dir = $this->getdirname($hash);
+                       if ($this->notSafeMode && !file_exists($dir)) {
+                               $oldu = umask(0);
+                               if (!@mkdir($dir,0771)) if(!is_dir($dir) && $debug) ADOConnection::outp("Cannot create $dir");
+                               umask($oldu);
+                       }
+               
+                       return $dir;
+               }
+               
+               /**
+               * Private function to erase all of the files and subdirectories in a directory.
+               *
+               * Just specify the directory, and tell it if you want to delete the directory or just clear it out.
+               * Note: $kill_top_level is used internally in the function to flush subdirectories.
+               */
+               function _dirFlush($dir, $kill_top_level = false) 
+               {
+                  if(!$dh = @opendir($dir)) return;
+                  
+                  while (($obj = readdir($dh))) {
+                               if($obj=='.' || $obj=='..') continue;
+                               $f = $dir.'/'.$obj;
+               
+                               if (strpos($obj,'.cache')) @unlink($f);
+                               if (is_dir($f)) $this->_dirFlush($f, true);
+                  }
+                  if ($kill_top_level === true) @rmdir($dir);
+                  return true;
+               }
+       }
+       
        //==============================================================================================        
        // CLASS ADOConnection
        //==============================================================================================        
                if ($argDatabaseName != "") $this->database = $argDatabaseName;         
                
                $this->_isPersistentConnection = false; 
+                       
+               global $ADODB_CACHE;
+               if (empty($ADODB_CACHE)) $this->_CreateCache();
+               
                if ($forceNew) {
                        if ($rez=$this->_nconnect($this->host, $this->user, $this->password, $this->database)) return true;
                } else {
         */     
        function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
        {
+               
                if (defined('ADODB_NEVER_PERSIST')) 
                        return $this->Connect($argHostname,$argUsername,$argPassword,$argDatabaseName);
                
                if ($argDatabaseName != "") $this->database = $argDatabaseName;         
                        
                $this->_isPersistentConnection = true;  
+               
+               global $ADODB_CACHE;
+               if (empty($ADODB_CACHE)) $this->_CreateCache();
+               
                if ($rez = $this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true;
                if (isset($rez)) {
                        $err = $this->ErrorMsg();
                ADOConnection::outp($msg);
        }
        
+       // create cache class. Code is backward compat with old memcache implementation
+       function _CreateCache()
+       {
+       global $ADODB_CACHE, $ADODB_CACHE_CLASS;
+       
+               if ($this->memCache) {
+               global $ADODB_INCLUDED_MEMCACHE;
+               
+                       if (empty($ADODB_INCLUDED_MEMCACHE)) include(ADODB_DIR.'/adodb-memcache.lib.inc.php');
+                               $ADODB_CACHE = new ADODB_Cache_MemCache($this);
+               } else
+                               $ADODB_CACHE = new $ADODB_CACHE_CLASS($this);
+               
+       }
+       
        // Format date column in sql string given an input format that understands Y M D
        function SQLDate($fmt, $col=false)
        {       
        {
                if ($this->transOff > 0) {
                        $this->transOff += 1;
-                       return;
+                       return true;
                }
                
                $this->_oldRaiseFn = $this->raiseErrorFn;
                $this->_transOK = true;
                
                if ($this->debug && $this->transCnt > 0) ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans");
-               $this->BeginTrans();
+               $ok = $this->BeginTrans();
                $this->transOff = 1;
+               return $ok;
        }
        
        
                        
                        if (!is_array($sql) && !$this->_bindInputArray) {
                                $sqlarr = explode('?',$sql);
-                                       
+                               $nparams = sizeof($sqlarr)-1;
                                if (!$array_2d) $inputarr = array($inputarr);
                                foreach($inputarr as $arr) {
                                        $sql = ''; $i = 0;
                                                else
                                                        $sql .= $v;
                                                $i += 1;
-                                       }
+                                               
+                                               if ($i == $nparams) break;
+                                       } // while
                                        if (isset($sqlarr[$i])) {
                                                $sql .= $sqlarr[$i];
                                                if ($i+1 != sizeof($sqlarr)) $this->outp_throw( "Input Array does not match ?: ".htmlspecialchars($sql),'Execute');
                $savec = $ADODB_COUNTRECS;
                $ADODB_COUNTRECS = false;
                        
-               if ($offset>0){
-                       if ($secs2cache != 0) $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
-                       else $rs = $this->Execute($sql,$inputarr);
-               } else {
-                       if ($secs2cache != 0) $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
-                       else $rs = $this->Execute($sql,$inputarr);
-               }
+
+               if ($secs2cache != 0) $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
+               else $rs = $this->Execute($sql,$inputarr);
+               
                $ADODB_COUNTRECS = $savec;
                if ($rs && !$rs->EOF) {
                        $rs = $this->_rs2rs($rs,$nrows,$offset);
        */
        function GetOne($sql,$inputarr=false)
        {
-       global $ADODB_COUNTRECS;
+       global $ADODB_COUNTRECS,$ADODB_GETONE_EOF;
                $crecs = $ADODB_COUNTRECS;
                $ADODB_COUNTRECS = false;
                
                $ret = false;
                $rs = $this->Execute($sql,$inputarr);
                if ($rs) {      
-                       if ($rs->EOF) $ret = null;
+                       if ($rs->EOF) $ret = $ADODB_GETONE_EOF;
                        else $ret = reset($rs->fields);
                        
                        $rs->Close();
                return $ret;
        }
        
+       // $where should include 'WHERE fld=value'
+       function GetMedian($table, $field,$where = '')
+       {
+               $total = $this->GetOne("select count(*) from $table $where");
+               if (!$total) return false;
+       
+               $midrow = (integer) ($total/2);
+               $rs = $this->SelectLimit("select $field from $table $where order by 1",1,$midrow);
+               if ($rs && !$rs->EOF) return reset($rs->fields);
+               return false;
+       }
+       
+       
        function CacheGetOne($secs2cache,$sql=false,$inputarr=false)
        {
+       global $ADODB_GETONE_EOF;
                $ret = false;
                $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
                if ($rs) {
-                       if ($rs->EOF) $ret = null;
+                       if ($rs->EOF) $ret = $ADODB_GETONE_EOF;
                        else $ret = reset($rs->fields);
                        $rs->Close();
                } 
        function GetRandRow($sql, $arr= false)
        {
                $rezarr = $this->GetAll($sql, $arr);
-               $sz = sizeof($rez);
+               $sz = sizeof($rezarr);
                return $rezarr[abs(rand()) % $sz];
        }
        
     */
        function CacheFlush($sql=false,$inputarr=false)
        {
-       global $ADODB_CACHE_DIR;
-       
-               if ($this->memCache) {
-               global $ADODB_INCLUDED_MEMCACHE;
+       global $ADODB_CACHE_DIR, $ADODB_CACHE;
                
-                       $key = false;
-                       if (empty($ADODB_INCLUDED_MEMCACHE)) include(ADODB_DIR.'/adodb-memcache.lib.inc.php');
-                       if ($sql) $key = $this->_gencachename($sql.serialize($inputarr),false,true);
-                       FlushMemCache($key, $this->memCacheHost, $this->memCachePort, $this->debug);
-                       return;
-               }
-       
-               if (strlen($ADODB_CACHE_DIR) > 1 && !$sql) {
-         /*if (strncmp(PHP_OS,'WIN',3) === 0)
-            $dir = str_replace('/', '\\', $ADODB_CACHE_DIR);
-         else */
-            $dir = $ADODB_CACHE_DIR;
-            
-         if ($this->debug) {
-            ADOConnection::outp( "CacheFlush: $dir<br><pre>\n". $this->_dirFlush($dir)."</pre>");
-         } else {
-            $this->_dirFlush($dir);
-         }
-         return;
-      } 
-      
-      global $ADODB_INCLUDED_CSV;
-      if (empty($ADODB_INCLUDED_CSV)) include(ADODB_DIR.'/adodb-csvlib.inc.php');
-      
-      $f = $this->_gencachename($sql.serialize($inputarr),false);
-      adodb_write_file($f,''); // is adodb_write_file needed?
-      if (!@unlink($f)) {
-         if ($this->debug) ADOConnection::outp( "CacheFlush: failed for $f");
-      }
-   }
-   
-   /**
-   * Private function to erase all of the files and subdirectories in a directory.
-   *
-   * Just specify the directory, and tell it if you want to delete the directory or just clear it out.
-   * Note: $kill_top_level is used internally in the function to flush subdirectories.
-   *
-   */
-   function _dirFlush($dir, $kill_top_level = false) 
-   {
-      if(!$dh = @opendir($dir)) return;
-      
-      while (($obj = readdir($dh))) {
-         if($obj=='.' || $obj=='..') continue;
-               $f = $dir.'/'.$obj;
-               
-               if (strpos($obj,'.cache')) @unlink($f);
-               if (is_dir($f)) $this->_dirFlush($f, true);
-      }
-      if ($kill_top_level === true) @rmdir($dir);
-      return true;
-   }
-   
-    // this only deletes .cache files
-       function xCacheFlush($sql=false,$inputarr=false)
-       {
-       global $ADODB_CACHE_DIR;
-       
-               if ($this->memCache) {
-                       global $ADODB_INCLUDED_MEMCACHE;
-                       $key = false;
-                       if (empty($ADODB_INCLUDED_MEMCACHE)) include(ADODB_DIR.'/adodb-memcache.lib.inc.php');
-                       if ($sql) $key = $this->_gencachename($sql.serialize($inputarr),false,true);
-                       flushmemCache($key, $this->memCacheHost, $this->memCachePort, $this->debug);
-                       return;
-               }
-
-               if (strlen($ADODB_CACHE_DIR) > 1 && !$sql) {
-                       if (strncmp(PHP_OS,'WIN',3) === 0) {
-                               $cmd = 'del /s '.str_replace('/','\\',$ADODB_CACHE_DIR).'\adodb_*.cache';
-                       } else {
-                               //$cmd = 'find "'.$ADODB_CACHE_DIR.'" -type f -maxdepth 1 -print0 | xargs -0 rm -f';
-                               $cmd = 'rm -rf '.$ADODB_CACHE_DIR.'/[0-9a-f][0-9a-f]/'; 
-                               // old version 'rm -f `find '.$ADODB_CACHE_DIR.' -name adodb_*.cache`';
-                       }
-                       if ($this->debug) {
-                               ADOConnection::outp( "CacheFlush: $cmd<br><pre>\n", system($cmd),"</pre>");
-                       } else {
-                               exec($cmd);
-                       }
-                       return;
-               } 
-               
-               global $ADODB_INCLUDED_CSV;
-               if (empty($ADODB_INCLUDED_CSV)) include(ADODB_DIR.'/adodb-csvlib.inc.php');
+               if (!$sql) {
+                        $ADODB_CACHE->flushall($this->debug);
+                return;
+           }
                
                $f = $this->_gencachename($sql.serialize($inputarr),false);
-               adodb_write_file($f,''); // is adodb_write_file needed?
-               if (!@unlink($f)) {
-                       if ($this->debug) ADOConnection::outp( "CacheFlush: failed for $f");
-               }
+               return $ADODB_CACHE->flushcache($f, $this->debug);
        }
+   
        
        /**
        * Private function to generate filename for caching.
        * Assuming that we can have 50,000 files per directory with good performance, 
        * then we can scale to 12.8 million unique cached recordsets. Wow!
        */
-       function _gencachename($sql,$createdir,$memcache=false)
+       function _gencachename($sql,$createdir)
        {
-       global $ADODB_CACHE_DIR;
-       static $notSafeMode;
+       global $ADODB_CACHE, $ADODB_CACHE_DIR;
                
                if ($this->fetchMode === false) { 
                global $ADODB_FETCH_MODE;
                        $mode = $this->fetchMode;
                }
                $m = md5($sql.$this->databaseType.$this->database.$this->user.$mode);
-               if ($memcache) return $m;
+               if (!$ADODB_CACHE->createdir) return $m;
+               if (!$createdir) $dir = $ADODB_CACHE->getdirname($m);
+               else $dir = $ADODB_CACHE->createdir($m, $this->debug);
                
-               if (!isset($notSafeMode)) $notSafeMode = !ini_get('safe_mode');
-               $dir = ($notSafeMode) ? $ADODB_CACHE_DIR.'/'.substr($m,0,2) : $ADODB_CACHE_DIR;
-                       
-               if ($createdir && $notSafeMode && !file_exists($dir)) {
-                       $oldu = umask(0);
-                       if (!@mkdir($dir,0771)) 
-                                if(!is_dir($dir) && $this->debug) ADOConnection::outp( "Unable to mkdir $dir for $sql");
-                       umask($oldu);
-               }
                return $dir.'/adodb_'.$m.'.cache';
        }
        
         */
        function CacheExecute($secs2cache,$sql=false,$inputarr=false)
        {
+       global $ADODB_CACHE;
+       
                if (!is_numeric($secs2cache)) {
                        $inputarr = $sql;
                        $sql = $secs2cache;
                } else
                        $sqlparam = $sql;
                        
-               if ($this->memCache) {
-                       global $ADODB_INCLUDED_MEMCACHE;
-                       if (empty($ADODB_INCLUDED_MEMCACHE)) include(ADODB_DIR.'/adodb-memcache.lib.inc.php');
-                       $md5file = $this->_gencachename($sql.serialize($inputarr),false,true);
-               } else {
-               global $ADODB_INCLUDED_CSV;
-                       if (empty($ADODB_INCLUDED_CSV)) include(ADODB_DIR.'/adodb-csvlib.inc.php');
-                       $md5file = $this->_gencachename($sql.serialize($inputarr),true);
-               }
-
+               
+               $md5file = $this->_gencachename($sql.serialize($inputarr),true);
                $err = '';
                
                if ($secs2cache > 0){
-                       if ($this->memCache)
-                               $rs = getmemCache($md5file,$err,$secs2cache, $this->memCacheHost, $this->memCachePort);
-                       else
-                               $rs = csv2rs($md5file,$err,$secs2cache,$this->arrayClass);
+                       $rs = $ADODB_CACHE->readcache($md5file,$err,$secs2cache,$this->arrayClass);
                        $this->numCacheHits += 1;
                } else {
                        $err='Timeout 1';
                        $rs = false;
                        $this->numCacheMisses += 1;
                }
-       
+               
                if (!$rs) {
                // no cached rs found
                        if ($this->debug) {
                        
                        $rs = $this->Execute($sqlparam,$inputarr);
 
-                       if ($rs && $this->memCache) {
-                               $rs = $this->_rs2rs($rs); // read entire recordset into memory immediately
-                               if(!putmemCache($md5file, $rs, $this->memCacheHost, $this->memCachePort, $this->memCacheCompress, $this->debug)) {
-                                       if ($fn = $this->raiseErrorFn)
-                                               $fn($this->databaseType,'CacheExecute',-32000,"Cache write error",$md5file,$sql,$this);
-                                       if ($this->debug) ADOConnection::outp( " Cache write error");
-                               }
-                       } else if ($rs) {
+                       if ($rs) {
+
                                $eof = $rs->EOF;
                                $rs = $this->_rs2rs($rs); // read entire recordset into memory immediately
+                               $rs->timeCreated = time(); // used by caching
                                $txt = _rs2serialize($rs,false,$sql); // serialize
        
-                               $ok = adodb_write_file($md5file,$txt,$this->debug);
+                               $ok = $ADODB_CACHE->writecache($md5file,$txt,$this->debug, $secs2cache);
                                if (!$ok) {
                                        if ($ok === false) {
                                                $em = 'Cache write error';
                                        $rs->connection = $this; // Pablo suggestion
                                }  
                                
-                       } else
-                       if (!$this->memCache)
-                               @unlink($md5file);
+                       } else if (!$this->memCache)
+                               $ADODB_CACHE->flushcache($md5file);
                } else {
                        $this->_errorMsg = '';
                        $this->_errorCode = 0;
                }
        }
 
-       function GetActiveRecordsClass($class, $table,$whereOrderBy=false,$bindarr=false, $primkeyArr=false)
+       /**
+        * GetActiveRecordsClass Performs an 'ALL' query 
+        * 
+        * @param mixed $class This string represents the class of the current active record
+        * @param mixed $table Table used by the active record object
+        * @param mixed $whereOrderBy Where, order, by clauses
+        * @param mixed $bindarr 
+        * @param mixed $primkeyArr 
+        * @param array $extra Query extras: limit, offset...
+        * @param mixed $relations Associative array: table's foreign name, "hasMany", "belongsTo"
+        * @access public
+        * @return void
+        */
+       function GetActiveRecordsClass(
+                       $class, $table,$whereOrderBy=false,$bindarr=false, $primkeyArr=false,
+                       $extra=array(),
+                       $relations=array())
        {
        global $_ADODB_ACTIVE_DBS;
-       
-               $save = $this->SetFetchMode(ADODB_FETCH_NUM);
-               if (empty($whereOrderBy)) $whereOrderBy = '1=1';
-               $rows = $this->GetAll("select * from ".$table.' WHERE '.$whereOrderBy,$bindarr);
-               $this->SetFetchMode($save);
-               
-               $false = false;
-               
-               if ($rows === false) {  
-                       return $false;
-               }
-               
-               
-               if (!isset($_ADODB_ACTIVE_DBS)) {
-                       include(ADODB_DIR.'/adodb-active-record.inc.php');
-               }       
-               if (!class_exists($class)) {
-                       $this->outp_throw("Unknown class $class in GetActiveRecordsClass()",'GetActiveRecordsClass');
-                       return $false;
-               }
-               $arr = array();
-               foreach($rows as $row) {
-               
-                       $obj = new $class($table,$primkeyArr,$this);
-                       if ($obj->ErrorMsg()){
-                               $this->_errorMsg = $obj->ErrorMsg();
-                               return $false;
-                       }
-                       $obj->Set($row);
-                       $arr[] = $obj;
-               }
-               return $arr;
+               ## reduce overhead of adodb.inc.php -- moved to adodb-active-record.inc.php
+               ## if adodb-active-recordx is loaded -- should be no issue as they will probably use Find()
+               if (!isset($_ADODB_ACTIVE_DBS))include_once(ADODB_DIR.'/adodb-active-record.inc.php');
+               return adodb_GetActiveRecordsClass($this, $class, $table, $whereOrderBy, $bindarr, $primkeyArr, $extra, $relations);
        }
        
        function GetActiveRecords($table,$where=false,$bindarr=false,$primkeyArr=false)
@@ -2420,10 +2434,11 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
         *
         * @return  date string in database date format
         */
-       function DBDate($d)
+       function DBDate($d, $isfld=false)
        {
                if (empty($d) && $d !== 0) return 'null';
-
+               if ($isfld) return $d;
+               
                if (is_string($d) && !is_numeric($d)) {
                        if ($d === 'null' || strncmp($d,"'",1) === 0) return $d;
                        if ($this->isoDates) return "'$d'";
@@ -2457,10 +2472,11 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
         *
         * @return  timestamp string in database timestamp format
         */
-       function DBTimeStamp($ts)
+       function DBTimeStamp($ts,$isfld=false)
        {
                if (empty($ts) && $ts !== 0) return 'null';
-
+               if ($isfld) return $ts;
+               
                # strlen(14) allows YYYYMMDDHHMMSS format
                if (!is_string($ts) || (is_numeric($ts) && strlen($ts)<14)) 
                        return adodb_date($this->fmtTimeStamp,$ts);
@@ -2719,12 +2735,12 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
                
            function key() 
                {
-               return $false;
+               return false;
            }
                
            function current() 
                {
-               return $false;
+               return false;
            }
                
            function next() 
@@ -3670,6 +3686,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
                'CHARACTER' => 'C',
                'INTERVAL' => 'C',  # Postgres
                'MACADDR' => 'C', # postgres
+               'VAR_STRING' => 'C', # mysql
                ##
                'LONGCHAR' => 'X',
                'TEXT' => 'X',
@@ -3693,6 +3710,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
                ##
                'UNIQUEIDENTIFIER' => 'C', # MS SQL Server
                ##
+               'SMALLDATETIME' => 'T',
                'TIME' => 'T',
                'TIMESTAMP' => 'T',
                'DATETIME' => 'T',
@@ -4139,8 +4157,12 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
                        if (strncmp($origdsn,'pdo',3) == 0) {
                                $sch = explode('_',$dsna['scheme']);
                                if (sizeof($sch)>1) {
+                               
                                        $dsna['host'] = isset($dsna['host']) ? rawurldecode($dsna['host']) : '';
-                                       $dsna['host'] = rawurlencode($sch[1].':host='.rawurldecode($dsna['host']));
+                                       if ($sch[1] == 'sqlite')
+                                               $dsna['host'] = rawurlencode($sch[1].':'.rawurldecode($dsna['host']));
+                                       else
+                                               $dsna['host'] = rawurlencode($sch[1].':host='.rawurldecode($dsna['host']));
                                        $dsna['scheme'] = 'pdo';
                                }
                        }
@@ -4172,7 +4194,9 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
                if (!empty($ADODB_NEWCONNECTION)) {
                        $obj = $ADODB_NEWCONNECTION($db);
 
-               } else {
+               } 
+               
+               if(empty($obj)) {
                
                        if (!isset($ADODB_LASTDB)) $ADODB_LASTDB = '';
                        if (empty($db)) $db = $ADODB_LASTDB;
@@ -4347,13 +4371,13 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
                @param printOrArr  Pass in a boolean to indicate print, or an $exception->trace array (assumes that print is true then).
                @param levels Number of levels to display
        */
-       function adodb_backtrace($printOrArr=true,$levels=9999)
+       function adodb_backtrace($printOrArr=true,$levels=9999,$ishtml=null)
        {
                global $ADODB_INCLUDED_LIB;
                if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
-               return _adodb_backtrace($printOrArr,$levels);
+               return _adodb_backtrace($printOrArr,$levels,0,$ishtml);
        }
 
 
 }
-?>
+?>
\ No newline at end of file
index 468c070026ef08ab930d6c06c9d1ec579c62f15d..e4d1c23a892b73a7b890182ca0f879627aa304c4 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /**
-  V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -49,7 +49,7 @@ class ADODB2_access extends ADODB_DataDict {
        }
        
        // return string must begin with space
-       function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint)
+       function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
        {
                if ($fautoinc) {
                        $ftype = 'COUNTER';
index 116aa5a5159787f496a7a89bad28d2a0fb18c505..4934c93394438207a220699684345ecda28182a6 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /**
-  V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -47,7 +47,7 @@ class ADODB2_db2 extends ADODB_DataDict {
        }
        
        // return string must begin with space
-       function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint)
+       function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
        {       
                $suffix = '';
                if ($fautoinc) return ' GENERATED ALWAYS AS IDENTITY'; # as identity start with 
index 5d14c72c40bec8cb6c6dbf769906572c4fecc570..9afc6c7841db671f9f3713434d315fbde15e5fd6 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /**
-  V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -93,7 +93,7 @@ class ADODB2_firebird extends ADODB_DataDict {
        }
        
 
-       function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
+       function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
        {
                $suffix = '';
                
index f376d970ec3d136b51d92f099c99a04dbd9b74c0..ccad1ba6ad95bff96401efc86f6f33723081a105 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /**
-  V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
index 656d211dd7e75440b3d47373adb40f3028af2aa0..3cdd52ff08abdfa1a3d7d288cdc31c815dba40b6 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /**
-  V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
index 34fd943c3619a23d5bda3d28870a07e6182f24ed..ae0731a86368156c7ac3492833f18f7765e20e5d 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /**
-  V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -62,7 +62,7 @@ class ADODB2_informix extends ADODB_DataDict {
        }
        
        // return string must begin with space
-       function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint)
+       function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
        {
                if ($fautoinc) {
                        $ftype = 'SERIAL';
index d9e59a3f912562d09d36266a6c1a8ad82b3cd09f..65c897e2964a69764e4a89952ff4f1b0282c7a8b 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /**
-  V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -69,7 +69,7 @@ class ADODB2_mssql extends ADODB_DataDict {
                case 'TINYINT': return  'I1';
                case 'SMALLINT': return 'I2';
                case 'BIGINT':  return  'I8';
-               
+               case 'SMALLDATETIME': return 'T';
                case 'REAL':
                case 'FLOAT': return 'F';
                default: return parent::MetaType($t,$len,$fieldobj);
@@ -151,7 +151,7 @@ class ADODB2_mssql extends ADODB_DataDict {
        }
        
        // return string must begin with space
-       function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint)
+       function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
        {       
                $suffix = '';
                if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault";
diff --git a/lib/adodb/datadict/datadict-mssqlnative.inc.php b/lib/adodb/datadict/datadict-mssqlnative.inc.php
new file mode 100644 (file)
index 0000000..2dac9e5
--- /dev/null
@@ -0,0 +1,282 @@
+<?php
+
+/**
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
+       
+  Set tabs to 4 for best viewing.
+*/
+
+/*
+In ADOdb, named quotes for MS SQL Server use ". From the MSSQL Docs:
+
+       Note Delimiters are for identifiers only. Delimiters cannot be used for keywords, 
+       whether or not they are marked as reserved in SQL Server.
+       
+       Quoted identifiers are delimited by double quotation marks ("):
+       SELECT * FROM "Blanks in Table Name"
+       
+       Bracketed identifiers are delimited by brackets ([ ]):
+       SELECT * FROM [Blanks In Table Name]
+       
+       Quoted identifiers are valid only when the QUOTED_IDENTIFIER option is set to ON. By default, 
+       the Microsoft OLE DB Provider for SQL Server and SQL Server ODBC driver set QUOTED_IDENTIFIER ON 
+       when they connect. 
+       
+       In Transact-SQL, the option can be set at various levels using SET QUOTED_IDENTIFIER, 
+       the quoted identifier option of sp_dboption, or the user options option of sp_configure.
+       
+       When SET ANSI_DEFAULTS is ON, SET QUOTED_IDENTIFIER is enabled.
+       
+       Syntax
+       
+               SET QUOTED_IDENTIFIER { ON | OFF }
+
+
+*/
+
+// security - hide paths
+if (!defined('ADODB_DIR')) die();
+
+class ADODB2_mssqlnative extends ADODB_DataDict {
+       var $databaseType = 'mssqlnative';
+       var $dropIndex = 'DROP INDEX %2$s.%1$s';
+       var $renameTable = "EXEC sp_rename '%s','%s'";
+       var $renameColumn = "EXEC sp_rename '%s.%s','%s'";
+
+       var $typeX = 'TEXT';  ## Alternatively, set it to VARCHAR(4000)
+       var $typeXL = 'TEXT';
+       
+       //var $alterCol = ' ALTER COLUMN ';
+       
+       function MetaType($t,$len=-1,$fieldobj=false)
+       {
+               if (is_object($t)) {
+                       $fieldobj = $t;
+                       $t = $fieldobj->type;
+                       $len = $fieldobj->max_length;
+               }
+               
+               $len = -1; // mysql max_length is not accurate
+               switch (strtoupper($t)) {
+               case 'R':
+               case 'INT': 
+               case 'INTEGER': return  'I';
+               case 'BIT':
+               case 'TINYINT': return  'I1';
+               case 'SMALLINT': return 'I2';
+               case 'BIGINT':  return  'I8';
+               
+               case 'REAL':
+               case 'FLOAT': return 'F';
+               default: return parent::MetaType($t,$len,$fieldobj);
+               }
+       }
+       
+       function ActualType($meta)
+       {
+               switch(strtoupper($meta)) {
+
+               case 'C': return 'VARCHAR';
+               case 'XL': return (isset($this)) ? $this->typeXL : 'TEXT';
+               case 'X': return (isset($this)) ? $this->typeX : 'TEXT'; ## could be varchar(8000), but we want compat with oracle
+               case 'C2': return 'NVARCHAR';
+               case 'X2': return 'NTEXT';
+               
+               case 'B': return 'IMAGE';
+                       
+               case 'D': return 'DATETIME';
+               case 'T': return 'DATETIME';
+               case 'L': return 'BIT';
+               
+               case 'R':               
+               case 'I': return 'INT'; 
+               case 'I1': return 'TINYINT';
+               case 'I2': return 'SMALLINT';
+               case 'I4': return 'INT';
+               case 'I8': return 'BIGINT';
+               
+               case 'F': return 'REAL';
+               case 'N': return 'NUMERIC';
+               default:
+                       return $meta;
+               }
+       }
+       
+       
+       function AddColumnSQL($tabname, $flds)
+       {
+               $tabname = $this->TableName ($tabname);
+               $f = array();
+               list($lines,$pkey) = $this->_GenFields($flds);
+               $s = "ALTER TABLE $tabname $this->addCol";
+               foreach($lines as $v) {
+                       $f[] = "\n $v";
+               }
+               $s .= implode(', ',$f);
+               $sql[] = $s;
+               return $sql;
+       }
+       
+       /*
+       function AlterColumnSQL($tabname, $flds)
+       {
+               $tabname = $this->TableName ($tabname);
+               $sql = array();
+               list($lines,$pkey) = $this->_GenFields($flds);
+               foreach($lines as $v) {
+                       $sql[] = "ALTER TABLE $tabname $this->alterCol $v";
+               }
+
+               return $sql;
+       }
+       */
+       
+       function DropColumnSQL($tabname, $flds)
+       {
+               $tabname = $this->TableName ($tabname);
+               if (!is_array($flds))
+                       $flds = explode(',',$flds);
+               $f = array();
+               $s = 'ALTER TABLE ' . $tabname;
+               foreach($flds as $v) {
+                       $f[] = "\n$this->dropCol ".$this->NameQuote($v);
+               }
+               $s .= implode(', ',$f);
+               $sql[] = $s;
+               return $sql;
+       }
+       
+       // return string must begin with space
+       function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
+       {       
+               $suffix = '';
+               if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault";
+               if ($fautoinc) $suffix .= ' IDENTITY(1,1)';
+               if ($fnotnull) $suffix .= ' NOT NULL';
+               else if ($suffix == '') $suffix .= ' NULL';
+               if ($fconstraint) $suffix .= ' '.$fconstraint;
+               return $suffix;
+       }
+       
+       /*
+CREATE TABLE 
+    [ database_name.[ owner ] . | owner. ] table_name 
+    ( { < column_definition > 
+        | column_name AS computed_column_expression 
+        | < table_constraint > ::= [ CONSTRAINT constraint_name ] }
+
+            | [ { PRIMARY KEY | UNIQUE } [ ,...n ] 
+    ) 
+
+[ ON { filegroup | DEFAULT } ] 
+[ TEXTIMAGE_ON { filegroup | DEFAULT } ] 
+
+< column_definition > ::= { column_name data_type } 
+    [ COLLATE < collation_name > ] 
+    [ [ DEFAULT constant_expression ] 
+        | [ IDENTITY [ ( seed , increment ) [ NOT FOR REPLICATION ] ] ]
+    ] 
+    [ ROWGUIDCOL] 
+    [ < column_constraint > ] [ ...n ] 
+
+< column_constraint > ::= [ CONSTRAINT constraint_name ] 
+    { [ NULL | NOT NULL ] 
+        | [ { PRIMARY KEY | UNIQUE } 
+            [ CLUSTERED | NONCLUSTERED ] 
+            [ WITH FILLFACTOR = fillfactor ] 
+            [ON {filegroup | DEFAULT} ] ] 
+        ] 
+        | [ [ FOREIGN KEY ] 
+            REFERENCES ref_table [ ( ref_column ) ] 
+            [ ON DELETE { CASCADE | NO ACTION } ] 
+            [ ON UPDATE { CASCADE | NO ACTION } ] 
+            [ NOT FOR REPLICATION ] 
+        ] 
+        | CHECK [ NOT FOR REPLICATION ] 
+        ( logical_expression ) 
+    } 
+
+< table_constraint > ::= [ CONSTRAINT constraint_name ] 
+    { [ { PRIMARY KEY | UNIQUE } 
+        [ CLUSTERED | NONCLUSTERED ] 
+        { ( column [ ASC | DESC ] [ ,...n ] ) } 
+        [ WITH FILLFACTOR = fillfactor ] 
+        [ ON { filegroup | DEFAULT } ] 
+    ] 
+    | FOREIGN KEY 
+        [ ( column [ ,...n ] ) ] 
+        REFERENCES ref_table [ ( ref_column [ ,...n ] ) ] 
+        [ ON DELETE { CASCADE | NO ACTION } ] 
+        [ ON UPDATE { CASCADE | NO ACTION } ] 
+        [ NOT FOR REPLICATION ] 
+    | CHECK [ NOT FOR REPLICATION ] 
+        ( search_conditions ) 
+    } 
+
+
+       */
+       
+       /*
+       CREATE [ UNIQUE ] [ CLUSTERED | NONCLUSTERED ] INDEX index_name 
+    ON { table | view } ( column [ ASC | DESC ] [ ,...n ] ) 
+               [ WITH < index_option > [ ,...n] ] 
+               [ ON filegroup ]
+               < index_option > :: = 
+                   { PAD_INDEX | 
+                       FILLFACTOR = fillfactor | 
+                       IGNORE_DUP_KEY | 
+                       DROP_EXISTING | 
+                   STATISTICS_NORECOMPUTE | 
+                   SORT_IN_TEMPDB  
+               }
+*/
+       function _IndexSQL($idxname, $tabname, $flds, $idxoptions)
+       {
+               $sql = array();
+               
+               if ( isset($idxoptions['REPLACE']) || isset($idxoptions['DROP']) ) {
+                       $sql[] = sprintf ($this->dropIndex, $idxname, $tabname);
+                       if ( isset($idxoptions['DROP']) )
+                               return $sql;
+               }
+               
+               if ( empty ($flds) ) {
+                       return $sql;
+               }
+               
+               $unique = isset($idxoptions['UNIQUE']) ? ' UNIQUE' : '';
+               $clustered = isset($idxoptions['CLUSTERED']) ? ' CLUSTERED' : '';
+               
+               if ( is_array($flds) )
+                       $flds = implode(', ',$flds);
+               $s = 'CREATE' . $unique . $clustered . ' INDEX ' . $idxname . ' ON ' . $tabname . ' (' . $flds . ')';
+               
+               if ( isset($idxoptions[$this->upperName]) )
+                       $s .= $idxoptions[$this->upperName];
+               
+
+               $sql[] = $s;
+               
+               return $sql;
+       }
+       
+       
+       function _GetSize($ftype, $ty, $fsize, $fprec)
+       {
+               switch ($ftype) {
+               case 'INT':
+               case 'SMALLINT':
+               case 'TINYINT':
+               case 'BIGINT':
+                       return $ftype;
+               }
+       if ($ty == 'T') return $ftype;
+       return parent::_GetSize($ftype, $ty, $fsize, $fprec);    
+
+       }
+}
+?>
\ No newline at end of file
index 27e0b09e12ffa72965d800ab2e8a2673094e47ce..2f7426b5781e3406a9fd8b9ae54da92648db0915 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /**
-  V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -107,7 +107,7 @@ class ADODB2_mysql extends ADODB_DataDict {
        }
        
        // return string must begin with space
-       function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
+       function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
        {       
                $suffix = '';
                if ($funsigned) $suffix .= ' UNSIGNED';
index 4b7884706f2a2d0093dfb7b457201477d4fb8b7a..ec24595d6dd485e9d36af9a37c409ef6cf4a1b24 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /**
-  V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -82,15 +82,16 @@ class ADODB2_oci8 extends ADODB_DataDict {
                        
                case 'D': 
                case 'T': return 'DATE';
-               case 'L': return 'DECIMAL(1)';
-               case 'I1': return 'DECIMAL(3)';
-               case 'I2': return 'DECIMAL(5)';
+               case 'L': return 'NUMBER(1)';
+               case 'I1': return 'NUMBER(3)';
+               case 'I2': return 'NUMBER(5)';
                case 'I':
-               case 'I4': return 'DECIMAL(10)';
+               case 'I4': return 'NUMBER(10)';
                
-               case 'I8': return 'DECIMAL(20)';
-               case 'F': return 'DECIMAL';
-               case 'N': return 'DECIMAL';
+               case 'I8': return 'NUMBER(20)';
+               case 'F': return 'NUMBER';
+               case 'N': return 'NUMBER';
+               case 'R': return 'NUMBER(20)';
                default:
                        return $meta;
                }       
@@ -156,7 +157,7 @@ class ADODB2_oci8 extends ADODB_DataDict {
        }
        
        // return string must begin with space
-       function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
+       function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
        {
                $suffix = '';
                
index af948e1778edcebdb60a9c08f2cd609e0b025659..9121fa87525774e8e0b0ede6bf43db3f4a6e77ca 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /**
-  V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -326,7 +326,7 @@ class ADODB2_postgres extends ADODB_DataDict {
        }
 
        // return string must begin with space
-       function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint)
+       function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
        {
                if ($fautoinc) {
                        $ftype = 'SERIAL';
index bf52455476c8741e355538056503fbf0bd769888..76331e5f08fd2aef57e3d1b8f40285e57a414aad 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /**
-  V4.50 6 July 2004  (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V4.50 6 July 2004  (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -80,7 +80,7 @@ class ADODB2_sapdb extends ADODB_DataDict {
        }
        
        // return string must begin with space
-       function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
+       function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
        {       
                $suffix = '';
                if ($funsigned) $suffix .= ' UNSIGNED';
index d0acc34a37e4455d6fd2edbe6642cec99f6c9412..26545a935bcd7857695f0df0dee7ae5057bc54d9 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /**
-  V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -113,7 +113,7 @@ class ADODB2_sybase extends ADODB_DataDict {
        }
        
        // return string must begin with space
-       function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint)
+       function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
        {       
                $suffix = '';
                if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault";
index 663a2f474353681a7c31af4cad0ac089161d4b29..61e06db391281932fe674bc36342eb21fed92ed6 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
index d300e9225782bfc5b840e92814330b9178b8ed2c..557c476d0946220d510e2f759037c95e0861319d 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
@@ -221,11 +221,27 @@ class ADODB_ado extends ADOConnection {
                        $oCmd->CommandText = $sql;
                        $oCmd->CommandType = 1;
 
-                       foreach($inputarr as $val) {
+      // Map by http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ado270/htm/mdmthcreateparam.asp
+      // Check issue http://bugs.php.net/bug.php?id=40664 !!!
+                       while(list(, $val) = each($inputarr)) {
+                               $type = gettype($val);
+                               $len=strlen($val);
+                               if ($type == 'boolean')
+                                       $this->adoParameterType = 11;
+                               else if ($type == 'integer')
+                                       $this->adoParameterType = 3;
+                               else if ($type == 'double')
+                                       $this->adoParameterType = 5;
+                               elseif ($type == 'string')
+                                       $this->adoParameterType = 202;
+                               else if (($val === null) || (!defined($val)))
+                                       $len=1;
+                               else
+                                       $this->adoParameterType = 130;
+                               
                                // name, type, direction 1 = input, len,
-                               $this->adoParameterType = 130;
-                               $p = $oCmd->CreateParameter('name',$this->adoParameterType,1,strlen($val),$val);
-                               //print $p->Type.' '.$p->value;
+                       $p = $oCmd->CreateParameter('name',$this->adoParameterType,1,$len,$val);
+
                                $oCmd->Parameters->Append($p);
                        }
                        $p = false;
index d914b93829ef1a39eec77525aee3844e5ebaa1db..54a6f197428488c2cd481ea698d62059a37b530b 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
@@ -70,7 +70,8 @@ class ADODB_ado extends ADOConnection {
                 } else {
                        $argDatabasename = '';
                        if ($argDBorProvider) $argProvider = $argDBorProvider;
-                       else $argProvider = 'MSDASQL';
+                       else if (stripos($argHostname,'PROVIDER') === false) /* full conn string is not in $argHostname */ 
+                               $argProvider = 'MSDASQL';
                }
                
                
@@ -117,6 +118,7 @@ class ADODB_ado extends ADOConnection {
                $dbc->CursorLocation = $this->_cursor_location;
                return  $dbc->State > 0;
                } catch (exception $e) {
+                       if ($this->debug);echo "<pre>",$argHostname,"\n",$e,"</pre>\n";
                }
                
                return false;
@@ -244,13 +246,28 @@ class ADODB_ado extends ADOConnection {
                        $oCmd->CommandText = $sql;
                        $oCmd->CommandType = 1;
 
-                       foreach($inputarr as $val) {
+                       while(list(, $val) = each($inputarr)) {
+                               $type = gettype($val);
+                               $len=strlen($val);
+                               if ($type == 'boolean')
+                                       $this->adoParameterType = 11;
+                               else if ($type == 'integer')
+                                       $this->adoParameterType = 3;
+                               else if ($type == 'double')
+                                       $this->adoParameterType = 5;
+                               elseif ($type == 'string')
+                                       $this->adoParameterType = 202;
+                               else if (($val === null) || (!defined($val)))
+                                       $len=1;
+                               else
+                                       $this->adoParameterType = 130;
+                               
                                // name, type, direction 1 = input, len,
-                               $this->adoParameterType = 130;
-                               $p = $oCmd->CreateParameter('name',$this->adoParameterType,1,strlen($val),$val);
-                               //print $p->Type.' '.$p->value;
+                       $p = $oCmd->CreateParameter('name',$this->adoParameterType,1,$len,$val);
+
                                $oCmd->Parameters->Append($p);
                        }
+                       
                        $p = false;
                        $rs = $oCmd->Execute();
                        $e = $dbc->Errors;
@@ -375,6 +392,8 @@ class ADORecordSet_ado extends ADORecordSet {
                
                $o= new ADOFieldObject();
                $rs = $this->_queryID;
+               if (!$rs) return false;
+               
                $f = $rs->Fields($fieldOffset);
                $o->name = $f->Name;
                $t = $f->Type;
@@ -406,8 +425,12 @@ class ADORecordSet_ado extends ADORecordSet {
        function _initrs()
        {
                $rs = $this->_queryID;
-               $this->_numOfRows = $rs->RecordCount;
                
+               try {
+                       $this->_numOfRows = $rs->RecordCount;
+               } catch (Exception $e) {
+                       $this->_numOfRows = -1;
+               }
                $f = $rs->Fields;
                $this->_numOfFields = $f->Count;
        }
@@ -669,7 +692,10 @@ class ADORecordSet_ado extends ADORecordSet {
 
        function _close() {
                $this->_flds = false;
+               try {
                @$this->_queryID->Close();// by Pete Dishman (peterd@telephonetics.co.uk)
+               } catch (Exception $e) {
+               }
                $this->_queryID = false;        
        }
 
index 05cb0e7ce6d8fb8f86a567ace180e51ee0bf7487..7640af30c3be99a3e716d57924ea1b6ee7f7189e 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
index 5a73e64b94f090f8bad002f43f3b109ec7e1b7e6..e9bb7c270a731e1ec8cbf5b2668b4f1b8e64bf59 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
index 040834eaedff7f6a2479326de8ca9ce2889755da..ce55f2871123dbe0f97205f7beab6eed1ecd9aed 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
index f699c045fdd4795df00a2d1ae343899c8c4deb07..4fe3100212ee61d8c578c68bfcad26ec42eae42d 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
index e99c71ef20e700aaa5516257ef74aac1e952396a..5ee8da4abffa725987532e204e72c283e69d0da0 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-  V5.04a 25 Mar 2008   (c) 2006 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.06 16 Oct 2008   (c) 2006 John Lim (jlim#natsoft.com). All rights reserved.
 
   This is a version of the ADODB driver for DB2.  It uses the 'ibm_db2' PECL extension
   for PHP (http://pecl.php.net/package/ibm_db2), which in turn requires DB2 V8.2.2 or
@@ -33,7 +33,7 @@ class ADODB_db2 extends ADOConnection {
        var $sysDate = 'CURRENT DATE';
        var $sysTimeStamp = 'CURRENT TIMESTAMP';
        
-       var $fmtTimeStamp = "'Y-m-d-H:i:s'";
+       var $fmtTimeStamp = "'Y-m-d H:i:s'";
        var $replaceQuote = "''"; // string to use to replace quotes
        var $dataProvider = "db2";
        var $hasAffectedRows = true;
@@ -44,7 +44,7 @@ class ADODB_db2 extends ADOConnection {
                                                                // breaking backward-compat
        var $_bindInputArray = false;   
        var $_genIDSQL = "VALUES NEXTVAL FOR %s";
-       var $_genSeqSQL = "CREATE SEQUENCE %s START WITH 1 NO MAXVALUE NO CYCLE";
+       var $_genSeqSQL = "CREATE SEQUENCE %s START WITH %s NO MAXVALUE NO CYCLE";
        var $_dropSeqSQL = "DROP SEQUENCE %s";
        var $_autocommit = true;
        var $_haserrorfunctions = true;
@@ -68,7 +68,7 @@ class ADODB_db2 extends ADOConnection {
                global $php_errormsg;
                
                if (!function_exists('db2_connect')) {
-                       ADOConnection::outp("Warning: The old ODBC based DB2 driver has been renamed 'odbc_db2'. This ADOdb driver calls PHP's native db2 extension.");
+                       ADOConnection::outp("Warning: The old ODBC based DB2 driver has been renamed 'odbc_db2'. This ADOdb driver calls PHP's native db2 extension which is not installed.");
                        return null;
                }
                // This needs to be set before the connect().
@@ -228,7 +228,7 @@ class ADODB_db2 extends ADOConnection {
        function CreateSequence($seqname='adodbseq',$start=1)
        {
                if (empty($this->_genSeqSQL)) return false;
-               $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname));
+               $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$start));
                if (!$ok) return false;
                return true;
        }
@@ -249,9 +249,9 @@ class ADODB_db2 extends ADOConnection {
        {       
                // if you have to modify the parameter below, your database is overloaded,
                // or you need to implement generation of id's yourself!
-               $num = $this->GetOne("VALUES NEXTVAL FOR $seq");
+                               $num = $this->GetOne("VALUES NEXTVAL FOR $seq");
                                return $num;
-                       }
+       }
 
 
        function ErrorMsg()
index 3c8a15a280787eee754464a4b3c1e1b8ac46e1d4..b40281b7b6029dc95076f167b689fa59024a224e 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
- @version V5.04a 25 Mar 2008  (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ @version V5.06 16 Oct 2008  (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
@@ -127,7 +127,7 @@ class ADODB_fbsql extends ADOConnection {
        
        
        // returns queryID or false
-       function _query($sql,$inputarr)
+       function _query($sql,$inputarr=false)
        {
                return fbsql_query("$sql;",$this->_connectionID);
        }
index 41b1d7be1a01360fcf8214372fdbdd8599b12825..09f7e4a65984e55bcd929a1f85c997e00292d371 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
index 05bbead64fa7890512fab165631ea8e3bc7ec4b2..dfa94f5d292d0e4570c296266fba9e231f2ae78d 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.  
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
index 964a2fc8eeab828b440b7d3371c613aedd41658c..1b2643b2f728472f481b1de8e2bbd76ea352c7ac 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
-* @version V5.04a 25 Mar 2008  (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+* @version V5.06 16 Oct 2008  (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
index d36f9d1598f3de01c335cc601455527b7936471b..25dee2b527ae530aac2d93fd911894bab19857cb 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim. All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim. 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.
@@ -284,7 +284,7 @@ class ADODB_informix72 extends ADOConnection {
        }
 */
        // returns query ID if successful, otherwise false
-       function _query($sql,$inputarr)
+       function _query($sql,$inputarr=false)
        {
        global $ADODB_COUNTRECS;
        
index 30156a2a5a8a93114301cd955374d3aa63bcacc7..db42b78b2ede53f55bda1c21265e226a2d3fa5bf 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
-  V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -55,13 +55,17 @@ class ADODB_ldap extends ADOConnection {
                
                if ( !function_exists( 'ldap_connect' ) ) return null;
                
-               $conn_info = array( $host,$this->port);
+               if (strpos('ldap://',$host) === 0 || strpos('ldaps://',$host) === 0) {
+                       $this->_connectionID = @ldap_connect($host);
+               } else {
+                       $conn_info = array( $host,$this->port);
                
-               if ( strstr( $host, ':' ) ) {
-                   $conn_info = split( ':', $host );
-               } 
+                       if ( strstr( $host, ':' ) ) {
+                           $conn_info = split( ':', $host );
+                       
                
-               $this->_connectionID = @ldap_connect( $conn_info[0], $conn_info[1] );
+                       $this->_connectionID = @ldap_connect( $conn_info[0], $conn_info[1] );
+               }
                if (!$this->_connectionID) {
                        $e = 'Could not connect to ' . $conn_info[0];
                        $this->_errorMsg = $e;
@@ -150,7 +154,7 @@ class ADODB_ldap extends ADOConnection {
        }
        
        /* returns _queryID or false */
-       function _query($sql,$inputarr)
+       function _query($sql,$inputarr=false)
        {
                $rs = @ldap_search( $this->_connectionID, $this->database, $sql );
                $this->_errorMsg = ($rs) ? '' : 'Search error on '.$sql.': '.ldap_error($this->_connectionID);
index 96e029ec12dc300ac6f79a02cfab8e97ccc6e950..65a1c20eb6e47e26a7430e040bb15f0ec0f9607b 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
@@ -103,6 +103,7 @@ class ADODB_mssql extends ADOConnection {
        var $identitySQL = 'select SCOPE_IDENTITY()'; // 'select SCOPE_IDENTITY'; # for mssql 2000
        var $uniqueOrderBy = true;
        var $_bindInputArray = true;
+       var $forceNewConnect = false;
        
        function ADODB_mssql() 
        {               
@@ -156,7 +157,7 @@ class ADODB_mssql extends ADOConnection {
             return $this->lastInsID; // InsID from sp_executesql call
         } else {
                        return $this->GetOne($this->identitySQL);
-        }
+               }
        }
 
        function _affectedrows()
@@ -210,7 +211,11 @@ class ADODB_mssql extends ADOConnection {
                if ($nrows > 0 && $offset <= 0) {
                        $sql = preg_replace(
                                '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop." $nrows ",$sql);
-                       $rs = $this->Execute($sql,$inputarr);
+                               
+                       if ($secs2cache)
+                               $rs = $this->CacheExecute($secs2cache, $sql, $inputarr);
+                       else
+                               $rs = $this->Execute($sql,$inputarr);
                } else
                        $rs = ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
        
@@ -283,8 +288,8 @@ class ADODB_mssql extends ADOConnection {
        {
                if ($this->transOff) return true; 
                $this->transCnt += 1;
-               $this->Execute('BEGIN TRAN');
-               return true;
+               $ok = $this->Execute('BEGIN TRAN');
+               return $ok;
        }
                
        function CommitTrans($ok=true) 
@@ -292,15 +297,15 @@ class ADODB_mssql extends ADOConnection {
                if ($this->transOff) return true; 
                if (!$ok) return $this->RollbackTrans();
                if ($this->transCnt) $this->transCnt -= 1;
-               $this->Execute('COMMIT TRAN');
-               return true;
+               $ok = $this->Execute('COMMIT TRAN');
+               return $ok;
        }
        function RollbackTrans()
        {
                if ($this->transOff) return true; 
                if ($this->transCnt) $this->transCnt -= 1;
-               $this->Execute('ROLLBACK TRAN');
-               return true;
+               $ok = $this->Execute('ROLLBACK TRAN');
+               return $ok;
        }
        
        function SetTransactionMode( $transaction_mode ) 
@@ -365,7 +370,7 @@ class ADODB_mssql extends ADOConnection {
 
                $indexes = array();
                while ($row = $rs->FetchRow()) {
-                       if (!$primary && $row[5]) continue;
+                       if ($primary && !$row[5]) continue;
                        
             $indexes[$row[0]]['unique'] = $row[6];
             $indexes[$row[0]]['columns'][] = $row[1];
@@ -508,11 +513,11 @@ order by constraint_name, referenced_table_name, keyno";
           else return -1;
        }
        
-       // returns true or false
-       function _connect($argHostname, $argUsername, $argPassword, $argDatabasename)
+       // returns true or false, newconnect supported since php 5.1.0.
+       function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$newconnect=false)
        {
                if (!function_exists('mssql_pconnect')) return null;
-               $this->_connectionID = mssql_connect($argHostname,$argUsername,$argPassword);
+               $this->_connectionID = mssql_connect($argHostname,$argUsername,$argPassword,$newconnect);
                if ($this->_connectionID === false) return false;
                if ($argDatabasename) return $this->SelectDB($argDatabasename);
                return true;    
@@ -535,6 +540,11 @@ order by constraint_name, referenced_table_name, keyno";
                return true;    
        }
        
+       function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
+    {
+               return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename, true);
+    }
+
        function Prepare($sql)
        {
                $sqlarr = explode('?',$sql);
@@ -659,7 +669,7 @@ order by constraint_name, referenced_table_name, keyno";
        }
        
        // returns query ID if successful, otherwise false
-       function _query($sql,$inputarr)
+       function _query($sql,$inputarr=false)
        {
                $this->_errorMsg = false;
                if (is_array($inputarr)) {
@@ -1051,4 +1061,4 @@ order by constraint_name, ordinal_position
 http://www.databasejournal.com/scripts/article.php/1440551
 */
 
-?>
+?>
\ No newline at end of file
index 380829828f435c73d87969a1d1a71b442db017a9..199b100450d88c4b38e8bee25bcc21c10fe6dcf5 100644 (file)
@@ -9,7 +9,7 @@
 // ADOdb  - Database Abstraction Library for PHP                         //
 //          http://adodb.sourceforge.net/                                //
 //                                                                       //
-// Copyright (C) 2000-2008 John Lim (jlim\@natsoft.com.my)               //
+// Copyright (C) 2000-2009 John Lim (jlim\@natsoft.com.my)               //
 //          All rights reserved.                                         //
 //          Released under both BSD license and LGPL library license.    //
 //          Whenever there is any discrepancy between the two licenses,  //
@@ -58,7 +58,7 @@ class ADODB_mssql_n extends ADODB_mssql {
                ADODB_mssql::ADODB_mssql();
        }
 
-       function _query($sql,$inputarr)
+       function _query($sql,$inputarr=false)
        {
         $sql = $this->_appendN($sql);
                return ADODB_mssql::_query($sql,$inputarr);
diff --git a/lib/adodb/drivers/adodb-mssqlnative.inc.php b/lib/adodb/drivers/adodb-mssqlnative.inc.php
new file mode 100644 (file)
index 0000000..96885de
--- /dev/null
@@ -0,0 +1,922 @@
+<?php
+/* 
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
+Set tabs to 4 for best viewing.
+  
+  Latest version is available at http://adodb.sourceforge.net
+  
+  Native mssql driver. Requires mssql client. Works on Windows. 
+    http://www.microsoft.com/sql/technologies/php/default.mspx
+  To configure for Unix, see 
+       http://phpbuilder.com/columns/alberto20000919.php3
+
+    $stream = sqlsrv_get_field($stmt, $index, SQLSRV_SQLTYPE_STREAM(SQLSRV_ENC_BINARY));
+    stream_filter_append($stream, "convert.iconv.ucs-2/utf-8"); // Voila, UTF-8 can be read directly from $stream
+
+*/
+
+// security - hide paths
+if (!defined('ADODB_DIR')) die();
+
+if (!function_exists('sqlsrv_configure')) {
+       die("mssqlnative extension not installed");
+}
+
+if (!function_exists('sqlsrv_set_error_handling')) {
+       function sqlsrv_set_error_handling($constant) {
+               sqlsrv_configure("WarningsReturnAsErrors", $constant);
+       }
+}
+if (!function_exists('sqlsrv_log_set_severity')) {
+       function sqlsrv_log_set_severity($constant) {
+               sqlsrv_configure("LogSeverity", $constant);
+       }
+}
+if (!function_exists('sqlsrv_log_set_subsystems')) {
+       function sqlsrv_log_set_subsystems($constant) {
+               sqlsrv_configure("LogSubsystems", $constant);
+       }
+}
+
+
+//----------------------------------------------------------------
+// MSSQL returns dates with the format Oct 13 2002 or 13 Oct 2002
+// and this causes tons of problems because localized versions of 
+// MSSQL will return the dates in dmy or  mdy order; and also the 
+// month strings depends on what language has been configured. The 
+// following two variables allow you to control the localization
+// settings - Ugh.
+//
+// MORE LOCALIZATION INFO
+// ----------------------
+// To configure datetime, look for and modify sqlcommn.loc, 
+//     typically found in c:\mssql\install
+// Also read :
+//      http://support.microsoft.com/default.aspx?scid=kb;EN-US;q220918
+// Alternatively use:
+//        CONVERT(char(12),datecol,120)
+//
+// Also if your month is showing as month-1, 
+//   e.g. Jan 13, 2002 is showing as 13/0/2002, then see
+//     http://phplens.com/lens/lensforum/msgs.php?id=7048&x=1
+//   it's a localisation problem.
+//----------------------------------------------------------------
+
+
+// has datetime converstion to YYYY-MM-DD format, and also mssql_fetch_assoc
+if (ADODB_PHPVER >= 0x4300) {
+// docs say 4.2.0, but testing shows only since 4.3.0 does it work!
+       ini_set('mssql.datetimeconvert',0); 
+} else {
+    global $ADODB_mssql_mths;          // array, months must be upper-case
+       $ADODB_mssql_date_order = 'mdy'; 
+       $ADODB_mssql_mths = array(
+               'JAN'=>1,'FEB'=>2,'MAR'=>3,'APR'=>4,'MAY'=>5,'JUN'=>6,
+               'JUL'=>7,'AUG'=>8,'SEP'=>9,'OCT'=>10,'NOV'=>11,'DEC'=>12);
+}
+
+//---------------------------------------------------------------------------
+// Call this to autoset $ADODB_mssql_date_order at the beginning of your code,
+// just after you connect to the database. Supports mdy and dmy only.
+// Not required for PHP 4.2.0 and above.
+function AutoDetect_MSSQL_Date_Order($conn)
+{
+    global $ADODB_mssql_date_order;
+       $adate = $conn->GetOne('select getdate()');
+       if ($adate) {
+               $anum = (int) $adate;
+               if ($anum > 0) {
+                       if ($anum > 31) {
+                               //ADOConnection::outp( "MSSQL: YYYY-MM-DD date format not supported currently");
+                       } else
+                               $ADODB_mssql_date_order = 'dmy';
+               } else
+                       $ADODB_mssql_date_order = 'mdy';
+       }
+}
+
+class ADODB_mssqlnative extends ADOConnection {
+       var $databaseType = "mssqlnative";      
+       var $dataProvider = "mssqlnative";
+       var $replaceQuote = "''"; // string to use to replace quotes
+       var $fmtDate = "'Y-m-d'";
+       var $fmtTimeStamp = "'Y-m-d H:i:s'";
+       var $hasInsertID = true;
+       var $substr = "substring";
+       var $length = 'len';
+       var $hasAffectedRows = true;
+       var $poorAffectedRows = false;
+       var $metaDatabasesSQL = "select name from sys.sysdatabases where name <> 'master'";
+       var $metaTablesSQL="select name,case when type='U' then 'T' else 'V' end from sysobjects where (type='U' or type='V') and (name not in ('sysallocations','syscolumns','syscomments','sysdepends','sysfilegroups','sysfiles','sysfiles1','sysforeignkeys','sysfulltextcatalogs','sysindexes','sysindexkeys','sysmembers','sysobjects','syspermissions','sysprotects','sysreferences','systypes','sysusers','sysalternates','sysconstraints','syssegments','REFERENTIAL_CONSTRAINTS','CHECK_CONSTRAINTS','CONSTRAINT_TABLE_USAGE','CONSTRAINT_COLUMN_USAGE','VIEWS','VIEW_TABLE_USAGE','VIEW_COLUMN_USAGE','SCHEMATA','TABLES','TABLE_CONSTRAINTS','TABLE_PRIVILEGES','COLUMNS','COLUMN_DOMAIN_USAGE','COLUMN_PRIVILEGES','DOMAINS','DOMAIN_CONSTRAINTS','KEY_COLUMN_USAGE','dtproperties'))";
+       var $metaColumnsSQL = # xtype==61 is datetime
+        "select c.name,t.name,c.length,
+           (case when c.xusertype=61 then 0 else c.xprec end),
+           (case when c.xusertype=61 then 0 else c.xscale end) 
+           from syscolumns c join systypes t on t.xusertype=c.xusertype join sysobjects o on o.id=c.id where o.name='%s'";
+       var $hasTop = 'top';            // support mssql SELECT TOP 10 * FROM TABLE
+       var $hasGenID = true;
+       var $sysDate = 'convert(datetime,convert(char,GetDate(),102),102)';
+       var $sysTimeStamp = 'GetDate()';
+       var $maxParameterLen = 4000;
+       var $arrayClass = 'ADORecordSet_array_mssqlnative';
+       var $uniqueSort = true;
+       var $leftOuter = '*=';
+       var $rightOuter = '=*';
+       var $ansiOuter = true; // for mssql7 or later
+       var $identitySQL = 'select SCOPE_IDENTITY()'; // 'select SCOPE_IDENTITY'; # for mssql 2000
+       var $uniqueOrderBy = true;
+       var $_bindInputArray = true;
+       var $_dropSeqSQL = "drop table %s";
+       
+       function ADODB_mssqlnative() 
+       {               
+        if ($this->debug) {
+            error_log("<pre>");
+            sqlsrv_set_error_handling( SQLSRV_ERRORS_LOG_ALL );
+            sqlsrv_log_set_severity( SQLSRV_LOG_SEVERITY_ALL );
+            sqlsrv_log_set_subsystems(SQLSRV_LOG_SYSTEM_ALL);
+            sqlsrv_configure('warnings_return_as_errors', 0);
+        } else {
+            sqlsrv_set_error_handling(0);
+            sqlsrv_log_set_severity(0);
+            sqlsrv_log_set_subsystems(SQLSRV_LOG_SYSTEM_ALL);
+            sqlsrv_configure('warnings_return_as_errors', 0);
+        }
+       }
+
+       function ServerInfo()
+       {
+       global $ADODB_FETCH_MODE;
+               if ($this->fetchMode === false) {
+                       $savem = $ADODB_FETCH_MODE;
+                       $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+               } else 
+                       $savem = $this->SetFetchMode(ADODB_FETCH_NUM);
+               $arrServerInfo = sqlsrv_server_info($this->_connectionID);
+               $arr['description'] = $arrServerInfo['SQLServerName'].' connected to '.$arrServerInfo['CurrentDatabase'];
+               $arr['version'] = $arrServerInfo['SQLServerVersion'];//ADOConnection::_findvers($arr['description']);
+               return $arr;
+       }
+       
+       function IfNull( $field, $ifNull ) 
+       {
+               return " ISNULL($field, $ifNull) "; // if MS SQL Server
+       }
+       
+       function _insertid()
+       {
+       // SCOPE_IDENTITY()
+       // Returns the last IDENTITY value inserted into an IDENTITY column in 
+       // the same scope. A scope is a module -- a stored procedure, trigger, 
+       // function, or batch. Thus, two statements are in the same scope if 
+       // they are in the same stored procedure, function, or batch.
+               return $this->GetOne($this->identitySQL);
+       }
+
+       function _affectedrows()
+       {
+        return sqlsrv_rows_affected($this->_queryID);
+       }
+       
+       function CreateSequence($seq='adodbseq',$start=1)
+       {
+               if($this->debug) error_log("<hr>CreateSequence($seq,$start)");
+        sqlsrv_begin_transaction($this->_connectionID);
+               $start -= 1;
+               $this->Execute("create table $seq (id int)");//was float(53)
+               $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
+               if (!$ok) {
+            if($this->debug) error_log("<hr>Error: ROLLBACK");
+            sqlsrv_rollback($this->_connectionID);
+                       return false;
+               }
+        sqlsrv_commit($this->_connectionID);
+               return true;
+       }
+
+       function GenID($seq='adodbseq',$start=1)
+       {
+        if($this->debug) error_log("<hr>GenID($seq,$start)");
+        sqlsrv_begin_transaction($this->_connectionID);
+               $ok = $this->Execute("update $seq with (tablock,holdlock) set id = id + 1");
+               if (!$ok) {
+                       $this->Execute("create table $seq (id int)");
+                       $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
+                       if (!$ok) {
+                if($this->debug) error_log("<hr>Error: ROLLBACK");
+                sqlsrv_rollback($this->_connectionID);
+                               return false;
+                       }
+                       sqlsrv_commit($this->_connectionID);
+                       return $start;
+               }
+               $num = $this->GetOne("select id from $seq");
+        sqlsrv_commit($this->_connectionID);
+        if($this->debug) error_log(" Returning: $num");
+               return $num;
+       }
+       
+       // Format date column in sql string given an input format that understands Y M D
+       function SQLDate($fmt, $col=false)
+       {       
+               if (!$col) $col = $this->sysTimeStamp;
+               $s = '';
+               
+               $len = strlen($fmt);
+               for ($i=0; $i < $len; $i++) {
+                       if ($s) $s .= '+';
+                       $ch = $fmt[$i];
+                       switch($ch) {
+                       case 'Y':
+                       case 'y':
+                               $s .= "datename(yyyy,$col)";
+                               break;
+                       case 'M':
+                               $s .= "convert(char(3),$col,0)";
+                               break;
+                       case 'm':
+                               $s .= "replace(str(month($col),2),' ','0')";
+                               break;
+                       case 'Q':
+                       case 'q':
+                               $s .= "datename(quarter,$col)";
+                               break;
+                       case 'D':
+                       case 'd':
+                               $s .= "replace(str(day($col),2),' ','0')";
+                               break;
+                       case 'h':
+                               $s .= "substring(convert(char(14),$col,0),13,2)";
+                               break;
+                       
+                       case 'H':
+                               $s .= "replace(str(datepart(hh,$col),2),' ','0')";
+                               break;
+                               
+                       case 'i':
+                               $s .= "replace(str(datepart(mi,$col),2),' ','0')";
+                               break;
+                       case 's':
+                               $s .= "replace(str(datepart(ss,$col),2),' ','0')";
+                               break;
+                       case 'a':
+                       case 'A':
+                               $s .= "substring(convert(char(19),$col,0),18,2)";
+                               break;
+                               
+                       default:
+                               if ($ch == '\\') {
+                                       $i++;
+                                       $ch = substr($fmt,$i,1);
+                               }
+                               $s .= $this->qstr($ch);
+                               break;
+                       }
+               }
+               return $s;
+       }
+
+       
+       function BeginTrans()
+       {
+               if ($this->transOff) return true; 
+               $this->transCnt += 1;
+        if ($this->debug) error_log('<hr>begin transaction');
+               sqlsrv_begin_transaction($this->_connectionID);
+               return true;
+       }
+               
+       function CommitTrans($ok=true) 
+       { 
+               if ($this->transOff) return true; 
+        if ($this->debug) error_log('<hr>commit transaction');
+               if (!$ok) return $this->RollbackTrans();
+               if ($this->transCnt) $this->transCnt -= 1;
+               sqlsrv_commit($this->_connectionID);
+               return true;
+       }
+       function RollbackTrans()
+       {
+               if ($this->transOff) return true; 
+        if ($this->debug) error_log('<hr>rollback transaction');
+               if ($this->transCnt) $this->transCnt -= 1;
+               sqlsrv_rollback($this->_connectionID);
+               return true;
+       }
+       
+       function SetTransactionMode( $transaction_mode ) 
+       {
+               $this->_transmode  = $transaction_mode;
+               if (empty($transaction_mode)) {
+                       $this->Execute('SET TRANSACTION ISOLATION LEVEL READ COMMITTED');
+                       return;
+               }
+               if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode;
+               $this->Execute("SET TRANSACTION ".$transaction_mode);
+       }
+       
+       /*
+               Usage:
+               
+               $this->BeginTrans();
+               $this->RowLock('table1,table2','table1.id=33 and table2.id=table1.id'); # lock row 33 for both tables
+               
+               # some operation on both tables table1 and table2
+               
+               $this->CommitTrans();
+               
+               See http://www.swynk.com/friends/achigrik/SQL70Locks.asp
+       */
+       function RowLock($tables,$where,$flds='top 1 null as ignore') 
+       {
+               if (!$this->transCnt) $this->BeginTrans();
+               return $this->GetOne("select $flds from $tables with (ROWLOCK,HOLDLOCK) where $where");
+       }
+        
+       function SelectDB($dbName) 
+       {
+               $this->database = $dbName;
+               $this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions
+               if ($this->_connectionID) {
+            $rs = $this->Execute('USE '.$dbName); 
+            if($rs) {
+                return true;
+            } else return false;               
+               }
+               else return false;      
+       }
+       
+       function ErrorMsg() 
+       {
+               $retErrors = sqlsrv_errors(SQLSRV_ERR_ALL);
+               if($retErrors != null) {
+                       foreach($retErrors as $arrError) {
+                               $this->_errorMsg .= "SQLState: ".$arrError[ 'SQLSTATE']."\n";
+                               $this->_errorMsg .= "Error Code: ".$arrError[ 'code']."\n";
+                               $this->_errorMsg .= "Message: ".$arrError[ 'message']."\n";
+                       }
+               } else {
+                       $this->_errorMsg = "No errors found";
+               }
+               return $this->_errorMsg;
+       }
+       
+       function ErrorNo() 
+       {
+               if ($this->_logsql && $this->_errorCode !== false) return $this->_errorCode;
+               $err = sqlsrv_errors(SQLSRV_ERR_ALL);
+        if($err[0]) return $err[0]['code'];
+        else return -1;
+       }
+       
+       // returns true or false
+       function _connect($argHostname, $argUsername, $argPassword, $argDatabasename)
+       {
+               if (!function_exists('sqlsrv_connect')) return null;
+        $connectionInfo = array("Database"=>$argDatabasename,'UID'=>$argUsername,'PWD'=>$argPassword);
+        if ($this->debug) error_log("<hr>connecting... hostname: $argHostname params: ".var_export($connectionInfo,true));
+        //if ($this->debug) error_log("<hr>_connectionID before: ".serialize($this->_connectionID));
+        if(!($this->_connectionID = sqlsrv_connect($argHostname,$connectionInfo))) { 
+            if ($this->debug) error_log( "<hr><b>errors</b>: ".print_r( sqlsrv_errors(), true));
+            return false;
+        }
+        //if ($this->debug) error_log(" _connectionID after: ".serialize($this->_connectionID));
+        //if ($this->debug) error_log("<hr>defined functions: <pre>".var_export(get_defined_functions(),true)."</pre>");
+               return true;    
+       }
+       
+       // returns true or false
+       function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
+       {
+               //return null;//not implemented. NOTE: Persistent connections have no effect if PHP is used as a CGI program. (FastCGI!)
+        return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename);
+       }
+       
+       function Prepare($sql)
+       {
+               $stmt = sqlsrv_prepare( $this->_connectionID, $sql);
+               if (!$stmt)  return $sql;
+               return array($sql,$stmt);
+       }
+       
+       // returns concatenated string
+    // MSSQL requires integers to be cast as strings
+    // automatically cast every datatype to VARCHAR(255)
+    // @author David Rogers (introspectshun)
+    function Concat()
+    {
+        $s = "";
+        $arr = func_get_args();
+
+        // Split single record on commas, if possible
+        if (sizeof($arr) == 1) {
+            foreach ($arr as $arg) {
+                $args = explode(',', $arg);
+            }
+            $arr = $args;
+        }
+
+        array_walk($arr, create_function('&$v', '$v = "CAST(" . $v . " AS VARCHAR(255))";'));
+        $s = implode('+',$arr);
+        if (sizeof($arr) > 0) return "$s";
+        
+               return '';
+    }
+       
+       /* 
+               Unfortunately, it appears that mssql cannot handle varbinary > 255 chars
+               So all your blobs must be of type "image".
+               
+               Remember to set in php.ini the following...
+               
+               ; Valid range 0 - 2147483647. Default = 4096. 
+               mssql.textlimit = 0 ; zero to pass through 
+
+               ; Valid range 0 - 2147483647. Default = 4096. 
+               mssql.textsize = 0 ; zero to pass through 
+       */
+       function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
+       {
+       
+               if (strtoupper($blobtype) == 'CLOB') {
+                       $sql = "UPDATE $table SET $column='" . $val . "' WHERE $where";
+                       return $this->Execute($sql) != false;
+               }
+               $sql = "UPDATE $table SET $column=0x".bin2hex($val)." WHERE $where";
+               return $this->Execute($sql) != false;
+       }
+       
+       // returns query ID if successful, otherwise false
+       function _query($sql,$inputarr=false)
+       {
+               $this->_errorMsg = false;
+               if (is_array($inputarr)) {
+            $rez = sqlsrv_query($this->_connectionID,$sql,$inputarr);
+               } else if (is_array($sql)) {
+            $rez = sqlsrv_query($this->_connectionID,$sql[1],$inputarr);
+               } else {
+                       $rez = sqlsrv_query($this->_connectionID,$sql);
+               }
+        if ($this->debug) error_log("<hr>running query: ".var_export($sql,true)."<hr>input array: ".var_export($inputarr,true)."<hr>result: ".var_export($rez,true));//"<hr>connection: ".serialize($this->_connectionID)
+        //fix for returning true on anything besides select statements
+        if (is_array($sql)) $sql = $sql[1];
+        $sql = ltrim($sql);
+        if(stripos($sql, 'SELECT') !== 0 && $rez !== false) {
+            if ($this->debug) error_log(" isn't a select query, returning boolean true");
+            return true;
+        }
+        //end fix
+        if(!$rez) $rez = false;
+               return $rez;
+       }
+       
+       // returns true or false
+       function _close()
+       { 
+               if ($this->transCnt) $this->RollbackTrans();
+               $rez = @sqlsrv_close($this->_connectionID);
+               $this->_connectionID = false;
+               return $rez;
+       }
+       
+       // mssql uses a default date like Dec 30 2000 12:00AM
+       static function UnixDate($v)
+       {
+               return ADORecordSet_array_mssql::UnixDate($v);
+       }
+       
+       static function UnixTimeStamp($v)
+       {
+               return ADORecordSet_array_mssql::UnixTimeStamp($v);
+       }       
+
+       function &MetaIndexes($table,$primary=false)
+       {
+               $table = $this->qstr($table);
+
+               $sql = "SELECT i.name AS ind_name, C.name AS col_name, USER_NAME(O.uid) AS Owner, c.colid, k.Keyno, 
+                       CASE WHEN I.indid BETWEEN 1 AND 254 AND (I.status & 2048 = 2048 OR I.Status = 16402 AND O.XType = 'V') THEN 1 ELSE 0 END AS IsPK,
+                       CASE WHEN I.status & 2 = 2 THEN 1 ELSE 0 END AS IsUnique
+                       FROM dbo.sysobjects o INNER JOIN dbo.sysindexes I ON o.id = i.id 
+                       INNER JOIN dbo.sysindexkeys K ON I.id = K.id AND I.Indid = K.Indid 
+                       INNER JOIN dbo.syscolumns c ON K.id = C.id AND K.colid = C.Colid
+                       WHERE LEFT(i.name, 8) <> '_WA_Sys_' AND o.status >= 0 AND O.Name LIKE $table
+                       ORDER BY O.name, I.Name, K.keyno";
+
+               global $ADODB_FETCH_MODE;
+               $save = $ADODB_FETCH_MODE;
+        $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+        if ($this->fetchMode !== FALSE) {
+               $savem = $this->SetFetchMode(FALSE);
+        }
+        
+        $rs = $this->Execute($sql);
+        if (isset($savem)) {
+               $this->SetFetchMode($savem);
+        }
+        $ADODB_FETCH_MODE = $save;
+
+        if (!is_object($rs)) {
+               return FALSE;
+        }
+
+               $indexes = array();
+               while ($row = $rs->FetchRow()) {
+                       if (!$primary && $row[5]) continue;
+                       
+            $indexes[$row[0]]['unique'] = $row[6];
+            $indexes[$row[0]]['columns'][] = $row[1];
+       }
+        return $indexes;
+       }
+       
+       function MetaForeignKeys($table, $owner=false, $upper=false)
+       {
+       global $ADODB_FETCH_MODE;
+       
+               $save = $ADODB_FETCH_MODE;
+               $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+               $table = $this->qstr(strtoupper($table));
+               
+               $sql = 
+            "select object_name(constid) as constraint_name,
+                   col_name(fkeyid, fkey) as column_name,
+                   object_name(rkeyid) as referenced_table_name,
+                   col_name(rkeyid, rkey) as referenced_column_name
+            from sysforeignkeys
+            where upper(object_name(fkeyid)) = $table
+            order by constraint_name, referenced_table_name, keyno";
+               
+               $constraints =& $this->GetArray($sql);
+               
+               $ADODB_FETCH_MODE = $save;
+               
+               $arr = false;
+               foreach($constraints as $constr) {
+                       //print_r($constr);
+                       $arr[$constr[0]][$constr[2]][] = $constr[1].'='.$constr[3]; 
+               }
+               if (!$arr) return false;
+               
+               $arr2 = false;
+               
+               foreach($arr as $k => $v) {
+                       foreach($v as $a => $b) {
+                               if ($upper) $a = strtoupper($a);
+                               $arr2[$a] = $b;
+                       }
+               }
+               return $arr2;
+       }
+
+       //From: Fernando Moreira <FMoreira@imediata.pt>
+       function MetaDatabases() 
+       { 
+           $this->SelectDB("master");
+        $rs =& $this->Execute($this->metaDatabasesSQL);
+        $rows = $rs->GetRows();
+        $ret = array();
+        for($i=0;$i<count($rows);$i++) {
+            $ret[] = $rows[$i][0];
+        }
+        $this->SelectDB($this->database);
+        if($ret)
+            return $ret;
+        else 
+            return false;
+       } 
+
+       // "Stein-Aksel Basma" <basma@accelero.no>
+       // tested with MSSQL 2000
+       function &MetaPrimaryKeys($table)
+       {
+       global $ADODB_FETCH_MODE;
+       
+               $schema = '';
+               $this->_findschema($table,$schema);
+               if (!$schema) $schema = $this->database;
+               if ($schema) $schema = "and k.table_catalog like '$schema%'"; 
+
+               $sql = "select distinct k.column_name,ordinal_position from information_schema.key_column_usage k,
+               information_schema.table_constraints tc 
+               where tc.constraint_name = k.constraint_name and tc.constraint_type =
+               'PRIMARY KEY' and k.table_name = '$table' $schema order by ordinal_position ";
+               
+               $savem = $ADODB_FETCH_MODE;
+               $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+               $a = $this->GetCol($sql);
+               $ADODB_FETCH_MODE = $savem;
+               
+               if ($a && sizeof($a)>0) return $a;
+               $false = false;
+               return $false;    
+       }
+
+       
+       function &MetaTables($ttype=false,$showSchema=false,$mask=false) 
+       {
+           if ($mask) {
+                       $save = $this->metaTablesSQL;
+                       $mask = $this->qstr(($mask));
+                       $this->metaTablesSQL .= " AND name like $mask";
+               }
+               $ret =& ADOConnection::MetaTables($ttype,$showSchema);
+
+               if ($mask) {
+                       $this->metaTablesSQL = $save;
+               }
+               return $ret;
+       }
+}
+       
+/*--------------------------------------------------------------------------------------
+        Class Name: Recordset
+--------------------------------------------------------------------------------------*/
+
+class ADORecordset_mssqlnative extends ADORecordSet {  
+
+       var $databaseType = "mssqlnative";
+       var $canSeek = false;
+       var $fieldOffset = 0;
+       // _mths works only in non-localised system
+       
+       function ADORecordset_mssqlnative($id,$mode=false)
+       {
+               if ($mode === false) { 
+                       global $ADODB_FETCH_MODE;
+                       $mode = $ADODB_FETCH_MODE;
+
+               }
+               $this->fetchMode = $mode;
+               return $this->ADORecordSet($id,$mode);
+       }
+       
+       
+       function _initrs()
+       {
+           global $ADODB_COUNTRECS;    
+        if ($this->connection->debug) error_log("(before) ADODB_COUNTRECS: {$ADODB_COUNTRECS} _numOfRows: {$this->_numOfRows} _numOfFields: {$this->_numOfFields}");
+        /*$retRowsAff = sqlsrv_rows_affected($this->_queryID);//"If you need to determine the number of rows a query will return before retrieving the actual results, appending a SELECT COUNT ... query would let you get that information, and then a call to next_result would move you to the "real" results."
+        error_log("rowsaff: ".serialize($retRowsAff));
+               $this->_numOfRows = ($ADODB_COUNTRECS)? $retRowsAff:-1;*/
+        $this->_numOfRows = -1;//not supported
+        $fieldmeta = sqlsrv_field_metadata($this->_queryID);
+        $this->_numOfFields = ($fieldmeta)? count($fieldmeta):-1;
+        if ($this->connection->debug) error_log("(after) _numOfRows: {$this->_numOfRows} _numOfFields: {$this->_numOfFields}");
+       }
+       
+
+       //Contributed by "Sven Axelsson" <sven.axelsson@bokochwebb.se>
+       // get next resultset - requires PHP 4.0.5 or later
+       function NextRecordSet()
+       {
+               if (!sqlsrv_next_result($this->_queryID)) return false;
+               $this->_inited = false;
+               $this->bind = false;
+               $this->_currentRow = -1;
+               $this->Init();
+               return true;
+       }
+
+       /* Use associative array to get fields array */
+       function Fields($colname)
+       {
+               if ($this->fetchMode != ADODB_FETCH_NUM) return $this->fields[$colname];
+               if (!$this->bind) {
+                       $this->bind = array();
+                       for ($i=0; $i < $this->_numOfFields; $i++) {
+                               $o = $this->FetchField($i);
+                               $this->bind[strtoupper($o->name)] = $i;
+                       }
+               }
+               
+               return $this->fields[$this->bind[strtoupper($colname)]];
+       }
+       
+       /*      Returns: an object containing field information. 
+               Get column information in the Recordset object. fetchField() can be used in order to obtain information about
+               fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
+               fetchField() is retrieved.      */
+
+       function &FetchField($fieldOffset = -1) 
+       {
+        if ($this->connection->debug) error_log("<hr>fetchfield: $fieldOffset, fetch array: <pre>".print_r($this->fields,true)."</pre> backtrace: ".adodb_backtrace(false));
+               if ($fieldOffset != -1) $this->fieldOffset = $fieldOffset;
+               $arrKeys = array_keys($this->fields);
+               if(array_key_exists($this->fieldOffset,$arrKeys) && !array_key_exists($arrKeys[$this->fieldOffset],$this->fields)) {
+                       $f = false;
+               } else {
+                       $f = $this->fields[ $arrKeys[$this->fieldOffset] ];
+                       if($fieldOffset == -1) $this->fieldOffset++;
+               }
+
+        if (empty($f)) {
+            $f = false;//PHP Notice: Only variable references should be returned by reference
+        }
+               return $f;
+       }
+       
+       function _seek($row) 
+       {
+               return false;//There is no support for cursors in the driver at this time.  All data is returned via forward-only streams.
+       }
+
+       // speedup
+       function MoveNext() 
+       {
+        if ($this->connection->debug) error_log("movenext()");
+        //if ($this->connection->debug) error_log("eof (beginning): ".$this->EOF);
+               if ($this->EOF) return false;
+               
+               $this->_currentRow++;
+        if ($this->connection->debug) error_log("_currentRow: ".$this->_currentRow);
+               
+               if ($this->_fetch()) return true;
+               $this->EOF = true;
+        //if ($this->connection->debug) error_log("eof (end): ".$this->EOF);
+               
+               return false;
+       }
+
+       
+       // INSERT UPDATE DELETE returns false even if no error occurs in 4.0.4
+       // also the date format has been changed from YYYY-mm-dd to dd MMM YYYY in 4.0.4. Idiot!
+       function _fetch($ignore_fields=false) 
+       {
+        if ($this->connection->debug) error_log("_fetch()");
+               if ($this->fetchMode & ADODB_FETCH_ASSOC) {
+                       if ($this->fetchMode & ADODB_FETCH_NUM) {
+                if ($this->connection->debug) error_log("fetch mode: both");
+                               $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_BOTH);
+                       } else {
+                if ($this->connection->debug) error_log("fetch mode: assoc");
+                               $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_ASSOC);
+                       }
+                       
+                       if (ADODB_ASSOC_CASE == 0) {
+                               foreach($this->fields as $k=>$v) {
+                                       $this->fields[strtolower($k)] = $v;
+                               }
+                       } else if (ADODB_ASSOC_CASE == 1) {
+                               foreach($this->fields as $k=>$v) {
+                                       $this->fields[strtoupper($k)] = $v;
+                               }
+                       }
+               } else {
+            if ($this->connection->debug) error_log("fetch mode: num");
+                       $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_NUMERIC);
+               }
+        if(is_array($this->fields) && array_key_exists(1,$this->fields) && !array_key_exists(0,$this->fields)) {//fix fetch numeric keys since they're not 0 based 
+            $arrFixed = array();
+            foreach($this->fields as $key=>$value) {
+                if(is_numeric($key)) {
+                    $arrFixed[$key-1] = $value;
+                } else {
+                    $arrFixed[$key] = $value;
+                }
+            }
+            //if($this->connection->debug) error_log("<hr>fixing non 0 based return array, old: ".print_r($this->fields,true)." new: ".print_r($arrFixed,true));
+            $this->fields = $arrFixed;
+        }
+               if(is_array($this->fields)) {
+                       foreach($this->fields as $key=>$value) {
+                               if (is_object($value) && method_exists($value, 'format')) {//is DateTime object
+                                       $this->fields[$key] = $value->format("Y-m-d\TH:i:s\Z");
+                               }
+                       }
+               }
+        if($this->fields === null) $this->fields = false;
+        if ($this->connection->debug) error_log("<hr>after _fetch, fields: <pre>".print_r($this->fields,true)." backtrace: ".adodb_backtrace(false));
+               return $this->fields;
+       }
+       
+    /* close() only needs to be called if you are worried about using too much memory while your script
+               is running. All associated result memory for the specified result identifier will automatically be freed.       */
+       function _close() 
+       {
+               $rez = sqlsrv_free_stmt($this->_queryID);       
+               $this->_queryID = false;
+               return $rez;
+       }
+
+       // mssql uses a default date like Dec 30 2000 12:00AM
+       static function UnixDate($v)
+       {
+               return ADORecordSet_array_mssqlnative::UnixDate($v);
+       }
+       
+       static function UnixTimeStamp($v)
+       {
+               return ADORecordSet_array_mssqlnative::UnixTimeStamp($v);
+       }
+}
+
+
+class ADORecordSet_array_mssqlnative extends ADORecordSet_array {
+       function ADORecordSet_array_mssqlnative($id=-1,$mode=false) 
+       {
+               $this->ADORecordSet_array($id,$mode);
+       }
+       
+               // mssql uses a default date like Dec 30 2000 12:00AM
+       static function UnixDate($v)
+       {
+       
+               if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixDate($v);
+               
+       global $ADODB_mssql_mths,$ADODB_mssql_date_order;
+       
+               //Dec 30 2000 12:00AM 
+               if ($ADODB_mssql_date_order == 'dmy') {
+                       if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
+                               return parent::UnixDate($v);
+                       }
+                       if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
+                       
+                       $theday = $rr[1];
+                       $themth =  substr(strtoupper($rr[2]),0,3);
+               } else {
+                       if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
+                               return parent::UnixDate($v);
+                       }
+                       if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
+                       
+                       $theday = $rr[2];
+                       $themth = substr(strtoupper($rr[1]),0,3);
+               }
+               $themth = $ADODB_mssql_mths[$themth];
+               if ($themth <= 0) return false;
+               // h-m-s-MM-DD-YY
+               return  mktime(0,0,0,$themth,$theday,$rr[3]);
+       }
+       
+       static function UnixTimeStamp($v)
+       {
+       
+               if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixTimeStamp($v);
+               
+           global $ADODB_mssql_mths,$ADODB_mssql_date_order;
+       
+               //Dec 30 2000 12:00AM
+                if ($ADODB_mssql_date_order == 'dmy') {
+                        if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})|"
+                       ,$v, $rr)) return parent::UnixTimeStamp($v);
+                       if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
+               
+                       $theday = $rr[1];
+                       $themth =  substr(strtoupper($rr[2]),0,3);
+               } else {
+                       if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})|"
+                       ,$v, $rr)) return parent::UnixTimeStamp($v);
+                       if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
+               
+                       $theday = $rr[2];
+                       $themth = substr(strtoupper($rr[1]),0,3);
+               }
+               
+               $themth = $ADODB_mssql_mths[$themth];
+               if ($themth <= 0) return false;
+               
+               switch (strtoupper($rr[6])) {
+               case 'P':
+                       if ($rr[4]<12) $rr[4] += 12;
+                       break;
+               case 'A':
+                       if ($rr[4]==12) $rr[4] = 0;
+                       break;
+               default:
+                       break;
+               }
+               // h-m-s-MM-DD-YY
+               return  mktime($rr[4],$rr[5],0,$themth,$theday,$rr[3]);
+       }
+}
+
+/*
+Code Example 1:
+
+select         object_name(constid) as constraint_name,
+               object_name(fkeyid) as table_name, 
+        col_name(fkeyid, fkey) as column_name,
+       object_name(rkeyid) as referenced_table_name,
+       col_name(rkeyid, rkey) as referenced_column_name
+from sysforeignkeys
+where object_name(fkeyid) = x
+order by constraint_name, table_name, referenced_table_name,  keyno
+
+Code Example 2:
+select         constraint_name,
+       column_name,
+       ordinal_position
+from information_schema.key_column_usage
+where constraint_catalog = db_name()
+and table_name = x
+order by constraint_name, ordinal_position
+
+http://www.databasejournal.com/scripts/article.php/1440551
+*/
+
+?>
\ No newline at end of file
index 1d7845e5d7ff8d65d064b825bf367d7c545658fa..d10e1b8455376344ddc8510fb9a3f872cf4deaf3 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
-* @version V5.04a 25 Mar 2008  (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+* @version V5.06 16 Oct 2008  (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -45,7 +45,7 @@ class ADODB_mssqlpo extends ADODB_mssql {
                return array($sql,$stmt);
        }
        
-       function _query($sql,$inputarr)
+       function _query($sql,$inputarr=false)
        {
                if (is_string($sql)) $sql = str_replace('||','+',$sql);
                return ADODB_mssql::_query($sql,$inputarr);
index a8b7e5a63e01be787a846cde20a2e09e4fce9f74..07f9524ab1ad5f2d91b9b36c4e46fc2c44376879 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -447,7 +447,7 @@ class ADODB_mysql extends ADOConnection {
                        $fld->not_null = ($rs->fields[2] != 'YES');
                        $fld->primary_key = ($rs->fields[3] == 'PRI');
                        $fld->auto_increment = (strpos($rs->fields[5], 'auto_increment') !== false);
-                       $fld->binary = (strpos($type,'blob') !== false);
+                       $fld->binary = (strpos($type,'blob') !== false || strpos($type,'binary') !== false);
                        $fld->unsigned = (strpos($type,'unsigned') !== false);
                        $fld->zerofill = (strpos($type,'zerofill') !== false);
 
@@ -499,7 +499,7 @@ class ADODB_mysql extends ADOConnection {
        }
        
        // returns queryID or false
-       function _query($sql,$inputarr)
+       function _query($sql,$inputarr=false)
        {
        //global $ADODB_COUNTRECS;
                //if($ADODB_COUNTRECS) 
@@ -559,8 +559,9 @@ class ADODB_mysql extends ADOConnection {
             $table = "$owner.$table";
          }
          $a_create_table = $this->getRow(sprintf('SHOW CREATE TABLE %s', $table));
-                if ($associative) $create_sql = $a_create_table["Create Table"];
-         else $create_sql  = $a_create_table[1];
+                if ($associative) {
+                       $create_sql = isset($a_create_table["Create Table"]) ? $a_create_table["Create Table"] : $a_create_table["Create View"];
+         } else $create_sql  = $a_create_table[1];
 
          $matches = array();
 
@@ -576,9 +577,12 @@ class ADODB_mysql extends ADOConnection {
                  $ref_table = strtoupper($ref_table);
              }
 
-             $foreign_keys[$ref_table] = array();
-             $num_fields = count($my_field);
-             for ( $j = 0;  $j < $num_fields;  $j ++ ) {
+                       // see https://sourceforge.net/tracker/index.php?func=detail&aid=2287278&group_id=42718&atid=433976
+                       if (!isset($foreign_keys[$ref_table])) {
+                               $foreign_keys[$ref_table] = array();
+                       }
+            $num_fields = count($my_field);
+            for ( $j = 0;  $j < $num_fields;  $j ++ ) {
                  if ( $associative ) {
                      $foreign_keys[$ref_table][$ref_field[$j]] = $my_field[$j];
                  } else {
@@ -733,6 +737,7 @@ class ADORecordSet_mysql extends ADORecordSet{
                case 'LONGBLOB': 
                case 'BLOB':
                case 'MEDIUMBLOB':
+               case 'BINARY':
                        return !empty($fieldobj->binary) ? 'B' : 'X';
                        
                case 'YEAR':
index 7315d0c9d0207af1a68157c64a84e65a2d0c89cd..72616f6f90132d48722aa660fa8e6c1ce05ec0d6 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -491,8 +491,9 @@ class ADODB_mysqli extends ADOConnection {
               $table = "$owner.$table";
            }
            $a_create_table = $this->getRow(sprintf('SHOW CREATE TABLE %s', $table));
-               if ($associative) $create_sql = $a_create_table["Create Table"];
-           else $create_sql  = $a_create_table[1];
+               if ($associative) {
+                       $create_sql = isset($a_create_table["Create Table"]) ? $a_create_table["Create Table"] : $a_create_table["Create View"];
+           } else $create_sql  = $a_create_table[1];
        
            $matches = array();
        
@@ -508,8 +509,11 @@ class ADODB_mysqli extends ADOConnection {
                    $ref_table = strtoupper($ref_table);
                }
        
-               $foreign_keys[$ref_table] = array();
-               $num_fields               = count($my_field);
+               // see https://sourceforge.net/tracker/index.php?func=detail&aid=2287278&group_id=42718&atid=433976
+                       if (!isset($foreign_keys[$ref_table])) {
+                               $foreign_keys[$ref_table] = array();
+                       }
+               $num_fields = count($my_field);
                for ( $j = 0;  $j < $num_fields;  $j ++ ) {
                    if ( $associative ) {
                        $foreign_keys[$ref_table][$ref_field[$j]] = $my_field[$j];
@@ -558,8 +562,8 @@ class ADODB_mysqli extends ADOConnection {
                                $fld->type = $query_array[1];
                                $arr = explode(",",$query_array[2]);
                                $fld->enums = $arr;
-                               $fld->max_length = max(array_map("strlen",explode(",",$query_array[2]))) - 2; // PHP >= 4.0.6
-                               $fld->max_length = ($fld->max_length == 0 ? 1 : $fld->max_length);
+                               $zlen = max(array_map("strlen",$arr)) - 2; // PHP >= 4.0.6
+                               $fld->max_length = ($zlen > 0) ? $zlen : 1;
                        } else {
                                $fld->type = $type;
                                $fld->max_length = -1;
@@ -632,7 +636,6 @@ class ADODB_mysqli extends ADOConnection {
        function Prepare($sql)
        {
                return $sql;
-               
                $stmt = $this->_connectionID->prepare($sql);
                if (!$stmt) {
                        echo $this->ErrorMsg();
@@ -646,8 +649,18 @@ class ADODB_mysqli extends ADOConnection {
        function _query($sql, $inputarr)
        {
        global $ADODB_COUNTRECS;
-               
+               // Move to the next recordset, or return false if there is none. In a stored proc
+               // call, mysqli_next_result returns true for the last "recordset", but mysqli_store_result
+               // returns false. I think this is because the last "recordset" is actually just the
+               // return value of the stored proc (ie the number of rows affected).
+               // Commented out for reasons of performance. You should retrieve every recordset yourself.
+               //      if (!mysqli_next_result($this->connection->_connectionID))      return false;
+       
                if (is_array($sql)) {
+               
+                       // Prepare() not supported because mysqli_stmt_execute does not return a recordset, but
+                       // returns as bound variables.
+               
                        $stmt = $sql[1];
                        $a = '';
                        foreach($inputarr as $k => $v) {
@@ -658,16 +671,28 @@ class ADODB_mysqli extends ADOConnection {
                        
                        $fnarr = array_merge( array($stmt,$a) , $inputarr);
                        $ret = call_user_func_array('mysqli_stmt_bind_param',$fnarr);
-
                        $ret = mysqli_stmt_execute($stmt);
                        return $ret;
                }
+               
+               /*
                if (!$mysql_res =  mysqli_query($this->_connectionID, $sql, ($ADODB_COUNTRECS) ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT)) {
                    if ($this->debug) ADOConnection::outp("Query: " . $sql . " failed. " . $this->ErrorMsg());
                    return false;
                }
                
                return $mysql_res;
+               */
+               
+               if( $rs = mysqli_multi_query($this->_connectionID, $sql.';') )//Contributed by "Geisel Sierote" <geisel#4up.com.br>
+               {
+                       $rs = ($ADODB_COUNTRECS) ? @mysqli_store_result( $this->_connectionID ) : @mysqli_use_result( $this->_connectionID );
+                       return $rs ? $rs : true; // mysqli_more_results( $this->_connectionID )
+               } else {
+                       if($this->debug)
+                       ADOConnection::outp("Query: " . $sql . " failed. " . $this->ErrorMsg());
+                       return false;
+               }
        }
 
        /*      Returns: the last error message from previous database operation        */      
@@ -821,9 +846,10 @@ class ADORecordSet_mysqli extends ADORecordSet{
        {       
                $fieldnr = $fieldOffset;
                if ($fieldOffset != -1) {
-                 $fieldOffset = mysqli_field_seek($this->_queryID, $fieldnr);
+                 $fieldOffset = @mysqli_field_seek($this->_queryID, $fieldnr);
                }
-               $o = mysqli_fetch_field($this->_queryID);
+               $o = @mysqli_fetch_field($this->_queryID);
+               if (!$o) return false;
                /* Properties of an ADOFieldObject as set by MetaColumns */
                $o->primary_key = $o->flags & MYSQLI_PRI_KEY_FLAG;
                $o->not_null = $o->flags & MYSQLI_NOT_NULL_FLAG;
@@ -872,6 +898,33 @@ class ADORecordSet_mysqli extends ADORecordSet{
          return true;
        }
                
+               
+       function NextRecordSet()
+       {
+       global $ADODB_COUNTRECS;
+       
+               mysqli_free_result($this->_queryID);
+               $this->_queryID = -1;
+               // Move to the next recordset, or return false if there is none. In a stored proc
+               // call, mysqli_next_result returns true for the last "recordset", but mysqli_store_result
+               // returns false. I think this is because the last "recordset" is actually just the
+               // return value of the stored proc (ie the number of rows affected).
+               if(!mysqli_next_result($this->connection->_connectionID)) {
+               return false;
+               }
+               // CD: There is no $this->_connectionID variable, at least in the ADO version I'm using
+               $this->_queryID = ($ADODB_COUNTRECS) ? @mysqli_store_result( $this->connection->_connectionID )
+                                               : @mysqli_use_result( $this->connection->_connectionID );
+               if(!$this->_queryID) {
+                       return false;
+               }
+               $this->_inited = false;
+               $this->bind = false;
+               $this->_currentRow = -1;
+               $this->Init();
+               return true;
+       }
+
        // 10% speedup to move MoveNext to child class
        // This is the only implementation that works now (23-10-2003).
        // Other functions return no or the wrong results.
index 37b1b80832a5f0a743aba0bed23ddb8043233e66..22c93598b3ac13066659bf04e9da4ae82a1997e7 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /*
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
index 8812f2b1ce233f0202d60b90229919587e1750c7..58ce56b7286a4cdb951425f611a8392138d68a28 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /*
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -62,18 +62,18 @@ class ADODB_mysqlt extends ADODB_mysql {
                if (!$ok) return $this->RollbackTrans();
                
                if ($this->transCnt) $this->transCnt -= 1;
-               $this->Execute('COMMIT');
+               $ok = $this->Execute('COMMIT');
                $this->Execute('SET AUTOCOMMIT=1');
-               return true;
+               return $ok ? true : false;
        }
        
        function RollbackTrans()
        {
                if ($this->transOff) return true;
                if ($this->transCnt) $this->transCnt -= 1;
-               $this->Execute('ROLLBACK');
+               $ok = $this->Execute('ROLLBACK');
                $this->Execute('SET AUTOCOMMIT=1');
-               return true;
+               return $ok ? true : false;
        }
        
        function RowLock($tables,$where='',$flds='1 as adodb_ignore') 
index 07256336fe7cb45e2457092e581fb9bb037222e7..372d288f49f7cdcd813bc171b8c892c59ec7a692 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
-  V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). All rights reserved.
  
   First cut at the Netezza Driver by Josh Eldridge joshuae74#hotmail.com
  Based on the previous postgres drivers.
index cce7e6b20e6e1577b87d98e06c33480bb92067b3..da7bbb6865b0aab80845071ef7372c5c8e1944d3 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
 
-  version V5.04a 25 Mar 2008  (c) 2000-2008 John Lim. All rights reserved.
+  version V5.06 16 Oct 2008  (c) 2000-2009 John Lim. All rights reserved.
 
   Released under both BSD license and Lesser GPL library license. 
   Whenever there is any discrepancy between the two licenses, 
@@ -81,7 +81,7 @@ class ADODB_oci8 extends ADOConnection {
        var $leftOuter = '';  // oracle wierdness, $col = $value (+) for LEFT OUTER, $col (+)= $value for RIGHT OUTER
        var $session_sharing_force_blob = false; // alter session on updateblob if set to true 
        var $firstrows = true; // enable first rows optimization on SelectLimit()
-       var $selectOffsetAlg1 = 100; // when to use 1st algorithm of selectlimit.
+       var $selectOffsetAlg1 = 1000; // when to use 1st algorithm of selectlimit.
        var $NLS_DATE_FORMAT = 'YYYY-MM-DD';  // To include time, use 'RRRR-MM-DD HH24:MI:SS'
        var $dateformat = 'YYYY-MM-DD'; // DBDate format
        var $useDBDateFormatForTextInput=false;
@@ -184,7 +184,7 @@ NATSOFT.DOMAIN =
        function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$mode=0)
        {
                if (!function_exists('OCIPLogon')) return null;
-               
+               #adodb_backtrace(); 
                
         $this->_errorMsg = false;
                $this->_errorCode = false;
@@ -200,6 +200,11 @@ NATSOFT.DOMAIN =
                                        $argHostport = empty($this->port)?  "1521" : $this->port;
                                }
                                
+                               if (strncasecmp($argDatabasename,'SID=',4) == 0) {
+                                       $argDatabasename = substr($argDatabasename,4);
+                                       $this->connectSID = true;
+                               }
+                               
                                if ($this->connectSID) {
                                        $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
                                        .")(PORT=$argHostport))(CONNECT_DATA=(SID=$argDatabasename)))";
@@ -272,10 +277,10 @@ NATSOFT.DOMAIN =
        }
        
        // format and return date string in database date format
-       function DBDate($d)
+       function DBDate($d,$isfld=false)
        {
                if (empty($d) && $d !== 0) return 'null';
-               
+               if ($isfld) return 'TO_DATE('.$d.",'".$this->dateformat."')";
                if (is_string($d)) $d = ADORecordSet::UnixDate($d);
                return "TO_DATE(".adodb_date($this->fmtDate,$d).",'".$this->dateformat."')";
        }
@@ -297,9 +302,10 @@ NATSOFT.DOMAIN =
        }
        
        // format and return date string in database timestamp format
-       function DBTimeStamp($ts)
+       function DBTimeStamp($ts,$isfld=false)
        {
                if (empty($ts) && $ts !== 0) return 'null';
+               if ($isfld) return 'TO_DATE(substr('.$ts.",1,19),'RRRR-MM-DD, HH24:MI:SS')";
                if (is_string($ts)) $ts = ADORecordSet::UnixTimeStamp($ts);
                return 'TO_DATE('.adodb_date("'Y-m-d H:i:s'",$ts).",'RRRR-MM-DD, HH24:MI:SS')";
        }
@@ -399,8 +405,10 @@ NATSOFT.DOMAIN =
                $this->autoCommit = false;
                $this->_commit = OCI_DEFAULT;
                
-               if ($this->_transmode) $this->Execute("SET TRANSACTION ".$this->_transmode);
-               return true;
+               if ($this->_transmode) $ok = $this->Execute("SET TRANSACTION ".$this->_transmode);
+               else $ok = true;
+               
+               return $ok ? true : false;
        }
        
        function CommitTrans($ok=true) 
@@ -573,7 +581,7 @@ NATSOFT.DOMAIN =
                                $sql = preg_replace('/^[ \t\n]*select/i','SELECT /*+FIRST_ROWS*/',$sql);
                }
                
-               if ($offset < $this->selectOffsetAlg1 && 0 < $nrows && $nrows < 1000) {
+               if ($offset == -1 || ($offset < $this->selectOffsetAlg1 && 0 < $nrows && $nrows < 1000)) {
                        if ($nrows > 0) {       
                                if ($offset > 0) $nrows += $offset;
                                //$inputarr['adodb_rownum'] = $nrows;
@@ -595,14 +603,14 @@ NATSOFT.DOMAIN =
                        
                         // Let Oracle return the name of the columns
                        $q_fields = "SELECT * FROM (".$sql.") WHERE NULL = NULL";
-                        
+               
                        $false = false;
                        if (! $stmt_arr = $this->Prepare($q_fields)) {
                                return $false;
                        }
                        $stmt = $stmt_arr[1];
                         
-                        if (is_array($inputarr)) {
+                       if (is_array($inputarr)) {
                                foreach($inputarr as $k => $v) {
                                        if (is_array($v)) {
                                                if (sizeof($v) == 2) // suggested by g.giunta@libero.
@@ -616,6 +624,7 @@ NATSOFT.DOMAIN =
                                                        $bindarr[$k] = $v;
                                                } else {                                // dynamic sql, so rebind every time
                                                        OCIBindByName($stmt,":$k",$inputarr[$k],$len);
+                                                       
                                                }
                                        }
                                }
@@ -634,16 +643,17 @@ NATSOFT.DOMAIN =
                        
                         OCIFreeStatement($stmt); 
                         $fields = implode(',', $cols);
-                        $nrows += $offset;
+                        if ($nrows <= 0) $nrows = 999999999999;
+                        else $nrows += $offset;
                         $offset += 1; // in Oracle rownum starts at 1
                        
                        if ($this->databaseType == 'oci8po') {
-                                        $sql = "SELECT $fields FROM".
+                                        $sql = "SELECT /*+ FIRST_ROWS */ $fields FROM".
                                          "(SELECT rownum as adodb_rownum, $fields FROM".
                                          " ($sql) WHERE rownum <= ?".
                                          ") WHERE adodb_rownum >= ?";
                                } else {
-                                        $sql = "SELECT $fields FROM".
+                                        $sql = "SELECT /*+ FIRST_ROWS */ $fields FROM".
                                          "(SELECT rownum as adodb_rownum, $fields FROM".
                                          " ($sql) WHERE rownum <= :adodb_nrows".
                                          ") WHERE adodb_rownum >= :adodb_offset";
@@ -710,7 +720,7 @@ NATSOFT.DOMAIN =
        }
        
        /**
-       * Usage:  store file pointed to by $var in a blob
+       * Usage:  store file pointed to by $val in a blob
        */
        function UpdateBlobFile($table,$column,$val,$where,$blobtype='BLOB')
        {
@@ -945,7 +955,7 @@ NATSOFT.DOMAIN =
                        if ($isOutput == false) {
                                $var = $this->BlobEncode($var);
                                $tmp->WriteTemporary($var);
-                               $this->_refLOBs[$numlob]['VAR'] = $var;
+                               $this->_refLOBs[$numlob]['VAR'] = &$var;
                                if ($this->debug) {
                                        ADOConnection::outp("<b>Bind</b>: LOB has been written to temp");
                                }
@@ -1010,7 +1020,7 @@ NATSOFT.DOMAIN =
                  $db->bind($stmt,1); $db->bind($stmt,2); $db->bind($stmt,3); 
                  $db->execute($stmt);
        */ 
-       function _query($sql,$inputarr)
+       function _query($sql,$inputarr=false)
        {
                if (is_array($sql)) { // is prepared sql
                        $stmt = $sql[1];
@@ -1122,6 +1132,32 @@ NATSOFT.DOMAIN =
                return false;
        }
        
+       // From Oracle Whitepaper: PHP Scalability and High Availability
+       function IsConnectionError($err)
+       {
+               switch($err) {
+                       case 378: /* buffer pool param incorrect */
+                       case 602: /* core dump */
+                       case 603: /* fatal error */
+                       case 609: /* attach failed */
+                       case 1012: /* not logged in */
+                       case 1033: /* init or shutdown in progress */
+                       case 1043: /* Oracle not available */
+                       case 1089: /* immediate shutdown in progress */
+                       case 1090: /* shutdown in progress */
+                       case 1092: /* instance terminated */
+                       case 3113: /* disconnect */
+                       case 3114: /* not connected */
+                       case 3122: /* closing window */
+                       case 3135: /* lost contact */
+                       case 12153: /* TNS: not connected */
+                       case 27146: /* fatal or instance terminated */
+                       case 28511: /* Lost RPC */
+                       return true;
+               }
+               return false;
+       }
+       
        // returns true or false
        function _close()
        {
diff --git a/lib/adodb/drivers/adodb-oci8.old.inc.php b/lib/adodb/drivers/adodb-oci8.old.inc.php
new file mode 100644 (file)
index 0000000..6e63c2a
--- /dev/null
@@ -0,0 +1,1532 @@
+<?php
+/*
+
+  version V5.04 13 Feb 2008  (c) 2000-2008 John Lim. 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.
+
+  Latest version is available at http://adodb.sourceforge.net
+  
+  Code contributed by George Fourlanos <fou@infomap.gr>
+  
+  13 Nov 2000 jlim - removed all ora_* references.
+*/
+
+// security - hide paths
+if (!defined('ADODB_DIR')) die();
+
+/*
+NLS_Date_Format
+Allows you to use a date format other than the Oracle Lite default. When a literal
+character string appears where a date value is expected, the Oracle Lite database
+tests the string to see if it matches the formats of Oracle, SQL-92, or the value
+specified for this parameter in the POLITE.INI file. Setting this parameter also
+defines the default format used in the TO_CHAR or TO_DATE functions when no
+other format string is supplied.
+
+For Oracle the default is dd-mon-yy or dd-mon-yyyy, and for SQL-92 the default is
+yy-mm-dd or yyyy-mm-dd.
+
+Using 'RR' in the format forces two-digit years less than or equal to 49 to be
+interpreted as years in the 21st century (2000\962049), and years over 50 as years in
+the 20th century (1950\961999). Setting the RR format as the default for all two-digit
+year entries allows you to become year-2000 compliant. For example:
+NLS_DATE_FORMAT='RR-MM-DD'
+
+You can also modify the date format using the ALTER SESSION command. 
+*/
+
+# define the LOB descriptor type for the given type
+# returns false if no LOB descriptor
+function oci_lob_desc($type) {
+       switch ($type) {
+               case OCI_B_BFILE: $result = OCI_D_FILE; break;
+               case OCI_B_CFILEE: $result = OCI_D_FILE; break;
+               case OCI_B_CLOB: $result = OCI_D_LOB; break;
+               case OCI_B_BLOB: $result = OCI_D_LOB; break;
+               case OCI_B_ROWID: $result = OCI_D_ROWID; break;
+               default: $result = false; break;
+       }
+       return $result;
+}
+
+class ADODB_oci8 extends ADOConnection {
+       var $databaseType = 'oci8';
+       var $dataProvider = 'oci8';
+       var $replaceQuote = "''"; // string to use to replace quotes
+       var $concat_operator='||';
+       var $sysDate = "TRUNC(SYSDATE)";
+       var $sysTimeStamp = 'SYSDATE';
+       var $metaDatabasesSQL = "SELECT USERNAME FROM ALL_USERS WHERE USERNAME NOT IN ('SYS','SYSTEM','DBSNMP','OUTLN') ORDER BY 1";
+       var $_stmt;
+       var $_commit = OCI_COMMIT_ON_SUCCESS;
+       var $_initdate = true; // init date to YYYY-MM-DD
+       var $metaTablesSQL = "select table_name,table_type from cat where table_type in ('TABLE','VIEW') and table_name not like 'BIN\$%'"; // bin$ tables are recycle bin tables
+       var $metaColumnsSQL = "select cname,coltype,width, SCALE, PRECISION, NULLS, DEFAULTVAL from col where tname='%s' order by colno"; //changed by smondino@users.sourceforge. net
+       var $_bindInputArray = true;
+       var $hasGenID = true;
+       var $_genIDSQL = "SELECT (%s.nextval) FROM DUAL";
+       var $_genSeqSQL = "CREATE SEQUENCE %s START WITH %s";
+       var $_dropSeqSQL = "DROP SEQUENCE %s";
+       var $hasAffectedRows = true;
+       var $random = "abs(mod(DBMS_RANDOM.RANDOM,10000001)/10000000)";
+       var $noNullStrings = false;
+       var $connectSID = false;
+       var $_bind = false;
+       var $_nestedSQL = true;
+       var $_hasOCIFetchStatement = false;
+       var $_getarray = false; // currently not working
+       var $leftOuter = '';  // oracle wierdness, $col = $value (+) for LEFT OUTER, $col (+)= $value for RIGHT OUTER
+       var $session_sharing_force_blob = false; // alter session on updateblob if set to true 
+       var $firstrows = true; // enable first rows optimization on SelectLimit()
+       var $selectOffsetAlg1 = 100; // when to use 1st algorithm of selectlimit.
+       var $NLS_DATE_FORMAT = 'YYYY-MM-DD';  // To include time, use 'RRRR-MM-DD HH24:MI:SS'
+       var $dateformat = 'YYYY-MM-DD'; // DBDate format
+       var $useDBDateFormatForTextInput=false;
+       var $datetime = false; // MetaType('DATE') returns 'D' (datetime==false) or 'T' (datetime == true)
+       var $_refLOBs = array();
+               
+       // var $ansiOuter = true; // if oracle9
+    
+       function ADODB_oci8() 
+       {
+               $this->_hasOCIFetchStatement = ADODB_PHPVER >= 0x4200;
+               if (defined('ADODB_EXTENSION')) $this->rsPrefix .= 'ext_';
+       }
+       
+       /*  function MetaColumns($table) added by smondino@users.sourceforge.net*/
+       function MetaColumns($table) 
+       {
+       global $ADODB_FETCH_MODE;
+       
+               $false = false;
+               $save = $ADODB_FETCH_MODE;
+               $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+               if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
+               
+               $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table)));
+               
+               if (isset($savem)) $this->SetFetchMode($savem);
+               $ADODB_FETCH_MODE = $save;
+               if (!$rs) {
+                       return $false;
+               }
+               $retarr = array();
+               while (!$rs->EOF) { //print_r($rs->fields);
+                       $fld = new ADOFieldObject();
+                       $fld->name = $rs->fields[0];
+                       $fld->type = $rs->fields[1];
+                       $fld->max_length = $rs->fields[2];
+                       $fld->scale = $rs->fields[3];
+                       if ($rs->fields[1] == 'NUMBER') {
+                               if ($rs->fields[3] == 0) $fld->type = 'INT';
+                       $fld->max_length = $rs->fields[4];
+               }       
+                       $fld->not_null = (strncmp($rs->fields[5], 'NOT',3) === 0);
+                       $fld->binary = (strpos($fld->type,'BLOB') !== false);
+                       $fld->default_value = $rs->fields[6];
+                       
+                       if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld;     
+                       else $retarr[strtoupper($fld->name)] = $fld;
+                       $rs->MoveNext();
+               }
+               $rs->Close();
+               if (empty($retarr))
+                       return  $false;
+               else 
+                       return $retarr;
+       }
+       
+       function Time()
+       {
+               $rs = $this->Execute("select TO_CHAR($this->sysTimeStamp,'YYYY-MM-DD HH24:MI:SS') from dual");
+               if ($rs && !$rs->EOF) return $this->UnixTimeStamp(reset($rs->fields));
+               
+               return false;
+       }
+/*
+
+  Multiple modes of connection are supported:
+  
+  a. Local Database
+    $conn->Connect(false,'scott','tiger');
+  
+  b. From tnsnames.ora
+    $conn->Connect(false,'scott','tiger',$tnsname); 
+    $conn->Connect($tnsname,'scott','tiger'); 
+  
+  c. Server + service name
+    $conn->Connect($serveraddress,'scott,'tiger',$service_name);
+  
+  d. Server + SID
+       $conn->connectSID = true;
+       $conn->Connect($serveraddress,'scott,'tiger',$SID);
+
+
+Example TNSName:
+---------------
+NATSOFT.DOMAIN =
+  (DESCRIPTION =
+       (ADDRESS_LIST =
+         (ADDRESS = (PROTOCOL = TCP)(HOST = kermit)(PORT = 1523))
+       )
+       (CONNECT_DATA =
+         (SERVICE_NAME = natsoft.domain)
+       )
+  )
+  
+  There are 3 connection modes, 0 = non-persistent, 1 = persistent, 2 = force new connection
+       
+*/
+       function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$mode=0)
+       {
+               if (!function_exists('OCIPLogon')) return null;
+               
+               
+        $this->_errorMsg = false;
+               $this->_errorCode = false;
+               
+               if($argHostname) { // added by Jorma Tuomainen <jorma.tuomainen@ppoy.fi>
+                       if (empty($argDatabasename)) $argDatabasename = $argHostname;
+                       else {
+                               if(strpos($argHostname,":")) {
+                                       $argHostinfo=explode(":",$argHostname);
+                                       $argHostname=$argHostinfo[0];
+                                       $argHostport=$argHostinfo[1];
+                               } else {
+                                       $argHostport = empty($this->port)?  "1521" : $this->port;
+                               }
+                               
+                               if ($this->connectSID) {
+                                       $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
+                                       .")(PORT=$argHostport))(CONNECT_DATA=(SID=$argDatabasename)))";
+                               } else
+                                       $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
+                                       .")(PORT=$argHostport))(CONNECT_DATA=(SERVICE_NAME=$argDatabasename)))";
+                       }
+               }
+                               
+               //if ($argHostname) print "<p>Connect: 1st argument should be left blank for $this->databaseType</p>";
+               if ($mode==1) {
+                       $this->_connectionID = ($this->charSet) ? 
+                               OCIPLogon($argUsername,$argPassword, $argDatabasename,$this->charSet)
+                               :
+                               OCIPLogon($argUsername,$argPassword, $argDatabasename)
+                               ;
+                       if ($this->_connectionID && $this->autoRollback)  OCIrollback($this->_connectionID);
+               } else if ($mode==2) {
+                       $this->_connectionID = ($this->charSet) ? 
+                               OCINLogon($argUsername,$argPassword, $argDatabasename,$this->charSet)
+                               :
+                               OCINLogon($argUsername,$argPassword, $argDatabasename);
+                               
+               } else {
+                       $this->_connectionID = ($this->charSet) ? 
+                               OCILogon($argUsername,$argPassword, $argDatabasename,$this->charSet)
+                               :
+                               OCILogon($argUsername,$argPassword, $argDatabasename);
+               }
+               if (!$this->_connectionID) return false;
+               if ($this->_initdate) {
+                       $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='".$this->NLS_DATE_FORMAT."'");
+               }
+               
+               // looks like: 
+               // Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production With the Partitioning option JServer Release 8.1.7.0.0 - Production
+               // $vers = OCIServerVersion($this->_connectionID);
+               // if (strpos($vers,'8i') !== false) $this->ansiOuter = true;
+               return true;
+       }
+       
+       function ServerInfo()
+       {
+               $arr['compat'] = $this->GetOne('select value from sys.database_compatible_level');
+               $arr['description'] = @OCIServerVersion($this->_connectionID);
+               $arr['version'] = ADOConnection::_findvers($arr['description']);
+               return $arr;
+       }
+               // returns true or false
+       function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
+       {
+               return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,1);
+       }
+       
+       // returns true or false
+       function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
+       {
+               return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,2);
+       }
+       
+       function _affectedrows()
+       {
+               if (is_resource($this->_stmt)) return @OCIRowCount($this->_stmt);
+               return 0;
+       }
+       
+       function IfNull( $field, $ifNull ) 
+       {
+               return " NVL($field, $ifNull) "; // if Oracle
+       }
+       
+       // format and return date string in database date format
+       function DBDate($d)
+       {
+               if (empty($d) && $d !== 0) return 'null';
+               
+               if (is_string($d)) $d = ADORecordSet::UnixDate($d);
+               return "TO_DATE(".adodb_date($this->fmtDate,$d).",'".$this->dateformat."')";
+       }
+
+       function BindDate($d)
+       {
+               $d = ADOConnection::DBDate($d);
+               if (strncmp($d,"'",1)) return $d;
+               
+               return substr($d,1,strlen($d)-2);
+       }
+       
+       function BindTimeStamp($d)
+       {
+               $d = ADOConnection::DBTimeStamp($d);
+               if (strncmp($d,"'",1)) return $d;
+               
+               return substr($d,1,strlen($d)-2);
+       }
+       
+       // format and return date string in database timestamp format
+       function DBTimeStamp($ts)
+       {
+               if (empty($ts) && $ts !== 0) return 'null';
+               if (is_string($ts)) $ts = ADORecordSet::UnixTimeStamp($ts);
+               return 'TO_DATE('.adodb_date("'Y-m-d H:i:s'",$ts).",'RRRR-MM-DD, HH24:MI:SS')";
+       }
+       
+       function RowLock($tables,$where,$flds='1 as ignore') 
+       {
+               if ($this->autoCommit) $this->BeginTrans();
+               return $this->GetOne("select $flds from $tables where $where for update");
+       }
+       
+       function MetaTables($ttype=false,$showSchema=false,$mask=false) 
+       {
+               if ($mask) {
+                       $save = $this->metaTablesSQL;
+                       $mask = $this->qstr(strtoupper($mask));
+                       $this->metaTablesSQL .= " AND upper(table_name) like $mask";
+               }
+               $ret = ADOConnection::MetaTables($ttype,$showSchema);
+               
+               if ($mask) {
+                       $this->metaTablesSQL = $save;
+               }
+               return $ret;
+       }
+       
+       // Mark Newnham 
+       function MetaIndexes ($table, $primary = FALSE, $owner=false)
+       {
+        // save old fetch mode
+        global $ADODB_FETCH_MODE;
+
+        $save = $ADODB_FETCH_MODE;
+        $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+
+        if ($this->fetchMode !== FALSE) {
+               $savem = $this->SetFetchMode(FALSE);
+        }
+
+               // get index details
+               $table = strtoupper($table);
+
+               // get Primary index
+               $primary_key = '';
+
+               $false = false;
+               $rs = $this->Execute(sprintf("SELECT * FROM ALL_CONSTRAINTS WHERE UPPER(TABLE_NAME)='%s' AND CONSTRAINT_TYPE='P'",$table));
+               if ($row = $rs->FetchRow())
+                  $primary_key = $row[1]; //constraint_name
+
+               if ($primary==TRUE && $primary_key=='') {
+                        if (isset($savem)) 
+                $this->SetFetchMode($savem);
+                       $ADODB_FETCH_MODE = $save;
+                       return $false; //There is no primary key
+               }
+
+        $rs = $this->Execute(sprintf("SELECT ALL_INDEXES.INDEX_NAME, ALL_INDEXES.UNIQUENESS, ALL_IND_COLUMNS.COLUMN_POSITION, ALL_IND_COLUMNS.COLUMN_NAME FROM ALL_INDEXES,ALL_IND_COLUMNS WHERE UPPER(ALL_INDEXES.TABLE_NAME)='%s' AND ALL_IND_COLUMNS.INDEX_NAME=ALL_INDEXES.INDEX_NAME",$table));
+
+               
+        if (!is_object($rs)) {
+                       if (isset($savem)) 
+                               $this->SetFetchMode($savem);
+                       $ADODB_FETCH_MODE = $save;
+            return $false;
+        }
+
+               $indexes = array ();
+        // parse index data into array
+
+        while ($row = $rs->FetchRow()) {
+                       if ($primary && $row[0] != $primary_key) continue;
+            if (!isset($indexes[$row[0]])) {
+                               $indexes[$row[0]] = array(
+                                  'unique' => ($row[1] == 'UNIQUE'),
+                                  'columns' => array()
+                               );
+            }
+            $indexes[$row[0]]['columns'][$row[2] - 1] = $row[3];
+        }
+
+        // sort columns by order in the index
+        foreach ( array_keys ($indexes) as $index ) {
+            ksort ($indexes[$index]['columns']);
+        }
+
+               if (isset($savem)) { 
+            $this->SetFetchMode($savem);
+                       $ADODB_FETCH_MODE = $save;
+               }
+        return $indexes;
+       }
+       
+       function BeginTrans()
+       {       
+               if ($this->transOff) return true;
+               $this->transCnt += 1;
+               $this->autoCommit = false;
+               $this->_commit = OCI_DEFAULT;
+               
+               if ($this->_transmode) $this->Execute("SET TRANSACTION ".$this->_transmode);
+               return true;
+       }
+       
+       function CommitTrans($ok=true) 
+       { 
+               if ($this->transOff) return true;
+               if (!$ok) return $this->RollbackTrans();
+               
+               if ($this->transCnt) $this->transCnt -= 1;
+               $ret = OCIcommit($this->_connectionID);
+               $this->_commit = OCI_COMMIT_ON_SUCCESS;
+               $this->autoCommit = true;
+               return $ret;
+       }
+       
+       function RollbackTrans()
+       {
+               if ($this->transOff) return true;
+               if ($this->transCnt) $this->transCnt -= 1;
+               $ret = OCIrollback($this->_connectionID);
+               $this->_commit = OCI_COMMIT_ON_SUCCESS;
+               $this->autoCommit = true;
+               return $ret;
+       }
+       
+       
+       function SelectDB($dbName) 
+       {
+               return false;
+       }
+
+       function ErrorMsg() 
+       {
+               if ($this->_errorMsg !== false) return $this->_errorMsg;
+
+               if (is_resource($this->_stmt)) $arr = @OCIError($this->_stmt);
+               if (empty($arr)) {
+                       if (is_resource($this->_connectionID)) $arr = @OCIError($this->_connectionID);
+                       else $arr = @OCIError();
+                       if ($arr === false) return '';
+               }
+               $this->_errorMsg = $arr['message'];
+               $this->_errorCode = $arr['code'];
+               return $this->_errorMsg;
+       }
+
+       function ErrorNo() 
+       {
+               if ($this->_errorCode !== false) return $this->_errorCode;
+               
+               if (is_resource($this->_stmt)) $arr = @OCIError($this->_stmt);
+               if (empty($arr)) {
+                       $arr = @OCIError($this->_connectionID);
+                       if ($arr == false) $arr = @OCIError();
+                       if ($arr == false) return '';
+               }
+               
+               $this->_errorMsg = $arr['message'];
+               $this->_errorCode = $arr['code'];
+               
+               return $arr['code'];
+       }
+       
+       // Format date column in sql string given an input format that understands Y M D
+       function SQLDate($fmt, $col=false)
+       {       
+               if (!$col) $col = $this->sysTimeStamp;
+               $s = 'TO_CHAR('.$col.",'";
+               
+               $len = strlen($fmt);
+               for ($i=0; $i < $len; $i++) {
+                       $ch = $fmt[$i];
+                       switch($ch) {
+                       case 'Y':
+                       case 'y':
+                               $s .= 'YYYY';
+                               break;
+                       case 'Q':
+                       case 'q':
+                               $s .= 'Q';
+                               break;
+                               
+                       case 'M':
+                               $s .= 'Mon';
+                               break;
+                               
+                       case 'm':
+                               $s .= 'MM';
+                               break;
+                       case 'D':
+                       case 'd':
+                               $s .= 'DD';
+                               break;
+                       
+                       case 'H':
+                               $s.= 'HH24';
+                               break;
+                               
+                       case 'h':
+                               $s .= 'HH';
+                               break;
+                               
+                       case 'i':
+                               $s .= 'MI';
+                               break;
+                       
+                       case 's':
+                               $s .= 'SS';
+                               break;
+                       
+                       case 'a':
+                       case 'A':
+                               $s .= 'AM';
+                               break;
+                               
+                       case 'w':
+                               $s .= 'D';
+                               break;
+                               
+                       case 'l':
+                               $s .= 'DAY';
+                               break;
+                               
+                        case 'W':
+                               $s .= 'WW';
+                               break;
+                               
+                       default:
+                       // handle escape characters...
+                               if ($ch == '\\') {
+                                       $i++;
+                                       $ch = substr($fmt,$i,1);
+                               }
+                               if (strpos('-/.:;, ',$ch) !== false) $s .= $ch;
+                               else $s .= '"'.$ch.'"';
+                               
+                       }
+               }
+               return $s. "')";
+       }
+       
+       function GetRandRow($sql, $arr = false)
+       {
+               $sql = "SELECT * FROM ($sql ORDER BY dbms_random.value) WHERE rownum = 1";
+               
+               return $this->GetRow($sql,$arr);
+       }
+       
+       /*
+       This algorithm makes use of
+       
+       a. FIRST_ROWS hint
+       The FIRST_ROWS hint explicitly chooses the approach to optimize response time, 
+       that is, minimum resource usage to return the first row. Results will be returned 
+       as soon as they are identified. 
+
+       b. Uses rownum tricks to obtain only the required rows from a given offset.
+        As this uses complicated sql statements, we only use this if the $offset >= 100. 
+        This idea by Tomas V V Cox.
+        
+        This implementation does not appear to work with oracle 8.0.5 or earlier. Comment
+        out this function then, and the slower SelectLimit() in the base class will be used.
+       */
+       function SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
+       {
+               // seems that oracle only supports 1 hint comment in 8i
+               if ($this->firstrows) {
+                       if (strpos($sql,'/*+') !== false)
+                               $sql = str_replace('/*+ ','/*+FIRST_ROWS ',$sql);
+                       else
+                               $sql = preg_replace('/^[ \t\n]*select/i','SELECT /*+FIRST_ROWS*/',$sql);
+               }
+               
+               if ($offset < $this->selectOffsetAlg1 && 0 < $nrows && $nrows < 1000) {
+                       if ($nrows > 0) {       
+                               if ($offset > 0) $nrows += $offset;
+                               //$inputarr['adodb_rownum'] = $nrows;
+                               if ($this->databaseType == 'oci8po') {
+                                       $sql = "select * from (".$sql.") where rownum <= ?";
+                               } else {
+                                       $sql = "select * from (".$sql.") where rownum <= :adodb_offset";
+                               } 
+                               $inputarr['adodb_offset'] = $nrows;
+                               $nrows = -1;
+                       }
+                       // note that $nrows = 0 still has to work ==> no rows returned
+
+                       $rs = ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
+                       return $rs;
+                       
+               } else {
+                        // Algorithm by Tomas V V Cox, from PEAR DB oci8.php
+                       
+                        // Let Oracle return the name of the columns
+                       $q_fields = "SELECT * FROM (".$sql.") WHERE NULL = NULL";
+                        
+                       $false = false;
+                       if (! $stmt_arr = $this->Prepare($q_fields)) {
+                               return $false;
+                       }
+                       $stmt = $stmt_arr[1];
+                        
+                        if (is_array($inputarr)) {
+                               foreach($inputarr as $k => $v) {
+                                       if (is_array($v)) {
+                                               if (sizeof($v) == 2) // suggested by g.giunta@libero.
+                                                       OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]);
+                                               else
+                                                       OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
+                                       } else {
+                                               $len = -1;
+                                               if ($v === ' ') $len = 1;
+                                               if (isset($bindarr)) {  // is prepared sql, so no need to ocibindbyname again
+                                                       $bindarr[$k] = $v;
+                                               } else {                                // dynamic sql, so rebind every time
+                                                       OCIBindByName($stmt,":$k",$inputarr[$k],$len);
+                                               }
+                                       }
+                               }
+                       }
+                       
+                        if (!OCIExecute($stmt, OCI_DEFAULT)) {
+                                OCIFreeStatement($stmt); 
+                                return $false;
+                        }
+                        
+                        $ncols = OCINumCols($stmt);
+                        for ( $i = 1; $i <= $ncols; $i++ ) {
+                                $cols[] = '"'.OCIColumnName($stmt, $i).'"';
+                        }
+                        $result = false;
+                       
+                        OCIFreeStatement($stmt); 
+                        $fields = implode(',', $cols);
+                        $nrows += $offset;
+                        $offset += 1; // in Oracle rownum starts at 1
+                       
+                       if ($this->databaseType == 'oci8po') {
+                                        $sql = "SELECT $fields FROM".
+                                         "(SELECT rownum as adodb_rownum, $fields FROM".
+                                         " ($sql) WHERE rownum <= ?".
+                                         ") WHERE adodb_rownum >= ?";
+                               } else {
+                                        $sql = "SELECT $fields FROM".
+                                         "(SELECT rownum as adodb_rownum, $fields FROM".
+                                         " ($sql) WHERE rownum <= :adodb_nrows".
+                                         ") WHERE adodb_rownum >= :adodb_offset";
+                               } 
+                               $inputarr['adodb_nrows'] = $nrows;
+                               $inputarr['adodb_offset'] = $offset;
+                               
+                       if ($secs2cache>0) $rs = $this->CacheExecute($secs2cache, $sql,$inputarr);
+                       else $rs = $this->Execute($sql,$inputarr);
+                       return $rs;
+               }
+       
+       }
+       
+       /**
+       * Usage:
+       * Store BLOBs and CLOBs
+       *
+       * Example: to store $var in a blob
+       *
+       *       $conn->Execute('insert into TABLE (id,ablob) values(12,empty_blob())');
+       *       $conn->UpdateBlob('TABLE', 'ablob', $varHoldingBlob, 'ID=12', 'BLOB');
+       *       
+       *       $blobtype supports 'BLOB' and 'CLOB', but you need to change to 'empty_clob()'.
+       *
+       *  to get length of LOB:
+       *       select DBMS_LOB.GETLENGTH(ablob) from TABLE
+       *
+       * If you are using CURSOR_SHARING = force, it appears this will case a segfault
+       * under oracle 8.1.7.0. Run:
+       *        $db->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT');
+       * before UpdateBlob() then...
+       */
+
+       function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
+       {
+               
+               //if (strlen($val) < 4000) return $this->Execute("UPDATE $table SET $column=:blob WHERE $where",array('blob'=>$val)) != false;
+               
+               switch(strtoupper($blobtype)) {
+               default: ADOConnection::outp("<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
+               case 'BLOB': $type = OCI_B_BLOB; break;
+               case 'CLOB': $type = OCI_B_CLOB; break;
+               }
+               
+               if ($this->databaseType == 'oci8po') 
+                       $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
+               else 
+                       $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
+               
+               $desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB);
+               $arr['blob'] = array($desc,-1,$type);
+               if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT');
+               $commit = $this->autoCommit;
+               if ($commit) $this->BeginTrans();
+               $rs = $this->_Execute($sql,$arr);
+               if ($rez = !empty($rs)) $desc->save($val);
+               $desc->free();
+               if ($commit) $this->CommitTrans();
+               if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=FORCE');
+               
+               if ($rez) $rs->Close();
+               return $rez;
+       }
+       
+       /**
+       * Usage:  store file pointed to by $var in a blob
+       */
+       function UpdateBlobFile($table,$column,$val,$where,$blobtype='BLOB')
+       {
+               switch(strtoupper($blobtype)) {
+               default: ADOConnection::outp( "<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
+               case 'BLOB': $type = OCI_B_BLOB; break;
+               case 'CLOB': $type = OCI_B_CLOB; break;
+               }
+               
+               if ($this->databaseType == 'oci8po') 
+                       $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
+               else 
+                       $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
+               
+               $desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB);
+               $arr['blob'] = array($desc,-1,$type);
+               
+               $this->BeginTrans();
+               $rs = ADODB_oci8::Execute($sql,$arr);
+               if ($rez = !empty($rs)) $desc->savefile($val);
+               $desc->free();
+               $this->CommitTrans();
+               
+               if ($rez) $rs->Close();
+               return $rez;
+       }
+
+               /**
+        * Execute SQL 
+        *
+        * @param sql           SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text)
+        * @param [inputarr]    holds the input data to bind to. Null elements will be set to null.
+        * @return              RecordSet or false
+        */
+       function Execute($sql,$inputarr=false) 
+       {
+               if ($this->fnExecute) {
+                       $fn = $this->fnExecute;
+                       $ret = $fn($this,$sql,$inputarr);
+                       if (isset($ret)) return $ret;
+               }
+               if ($inputarr) {
+                       #if (!is_array($inputarr)) $inputarr = array($inputarr);
+                       
+                       $element0 = reset($inputarr);
+                       
+                       # is_object check because oci8 descriptors can be passed in
+                       if (is_array($element0) && !is_object(reset($element0))) {
+                               if (is_string($sql))
+                                       $stmt = $this->Prepare($sql);
+                               else
+                                       $stmt = $sql;
+                                       
+                               foreach($inputarr as $arr) {
+                                       $ret = $this->_Execute($stmt,$arr);
+                                       if (!$ret) return $ret;
+                               }
+                       } else {
+                               $ret = $this->_Execute($sql,$inputarr);
+                       }
+                       
+               } else {
+                       $ret = $this->_Execute($sql,false);
+               }
+
+               return $ret;
+       }
+       
+       /*
+               Example of usage:
+               
+               $stmt = $this->Prepare('insert into emp (empno, ename) values (:empno, :ename)');
+       */
+       function Prepare($sql,$cursor=false)
+       {
+       static $BINDNUM = 0;
+       
+               $stmt = OCIParse($this->_connectionID,$sql);
+
+               if (!$stmt) {
+                       $this->_errorMsg = false;
+                       $this->_errorCode = false;
+                       $arr = @OCIError($this->_connectionID);
+                       if ($arr === false) return false;
+               
+                       $this->_errorMsg = $arr['message'];
+                       $this->_errorCode = $arr['code'];
+                       return false;
+               }
+               
+               $BINDNUM += 1;
+               
+               $sttype = @OCIStatementType($stmt);
+               if ($sttype == 'BEGIN' || $sttype == 'DECLARE') {
+                       return array($sql,$stmt,0,$BINDNUM, ($cursor) ? OCINewCursor($this->_connectionID) : false);
+               }
+               return array($sql,$stmt,0,$BINDNUM);
+       }
+       
+       /*
+               Call an oracle stored procedure and returns a cursor variable as a recordset. 
+               Concept by Robert Tuttle robert@ud.com
+               
+               Example:
+                       Note: we return a cursor variable in :RS2
+                       $rs = $db->ExecuteCursor("BEGIN adodb.open_tab(:RS2); END;",'RS2');
+                       
+                       $rs = $db->ExecuteCursor(
+                               "BEGIN :RS2 = adodb.getdata(:VAR1); END;", 
+                               'RS2',
+                               array('VAR1' => 'Mr Bean'));
+                       
+       */
+       function ExecuteCursor($sql,$cursorName='rs',$params=false)
+       {
+               if (is_array($sql)) $stmt = $sql;
+               else $stmt = ADODB_oci8::Prepare($sql,true); # true to allocate OCINewCursor
+       
+               if (is_array($stmt) && sizeof($stmt) >= 5) {
+                       $hasref = true;
+                       $ignoreCur = false;
+                       $this->Parameter($stmt, $ignoreCur, $cursorName, false, -1, OCI_B_CURSOR);
+                       if ($params) {
+                               foreach($params as $k => $v) {
+                                       $this->Parameter($stmt,$params[$k], $k);
+                               }
+                       }
+               } else
+                       $hasref = false;
+                       
+               $rs = $this->Execute($stmt);
+               if ($rs) {
+                       if ($rs->databaseType == 'array') OCIFreeCursor($stmt[4]);
+                       else if ($hasref) $rs->_refcursor = $stmt[4];
+               }
+               return $rs;
+       }
+       
+       /*
+               Bind a variable -- very, very fast for executing repeated statements in oracle. 
+               Better than using
+                       for ($i = 0; $i < $max; $i++) { 
+                               $p1 = ?; $p2 = ?; $p3 = ?;
+                               $this->Execute("insert into table (col0, col1, col2) values (:0, :1, :2)", 
+                                       array($p1,$p2,$p3));
+                       }
+               
+               Usage:
+                       $stmt = $DB->Prepare("insert into table (col0, col1, col2) values (:0, :1, :2)");
+                       $DB->Bind($stmt, $p1);
+                       $DB->Bind($stmt, $p2);
+                       $DB->Bind($stmt, $p3);
+                       for ($i = 0; $i < $max; $i++) { 
+                               $p1 = ?; $p2 = ?; $p3 = ?;
+                               $DB->Execute($stmt);
+                       }
+                       
+               Some timings:           
+                       ** Test table has 3 cols, and 1 index. Test to insert 1000 records
+                       Time 0.6081s (1644.60 inserts/sec) with direct OCIParse/OCIExecute
+                       Time 0.6341s (1577.16 inserts/sec) with ADOdb Prepare/Bind/Execute
+                       Time 1.5533s ( 643.77 inserts/sec) with pure SQL using Execute
+                       
+               Now if PHP only had batch/bulk updating like Java or PL/SQL...
+       
+               Note that the order of parameters differs from OCIBindByName,
+               because we default the names to :0, :1, :2
+       */
+       function Bind(&$stmt,&$var,$size=4000,$type=false,$name=false,$isOutput=false)
+       {
+               
+               if (!is_array($stmt)) return false;
+        
+        if (($type == OCI_B_CURSOR) && sizeof($stmt) >= 5) { 
+            return OCIBindByName($stmt[1],":".$name,$stmt[4],$size,$type);
+        }
+        
+               if ($name == false) {
+                       if ($type !== false) $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size,$type);
+                       else $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size); // +1 byte for null terminator
+                       $stmt[2] += 1;
+               } else if (oci_lob_desc($type)) {
+                       if ($this->debug) {
+                               ADOConnection::outp("<b>Bind</b>: name = $name");
+                       }
+            //we have to create a new Descriptor here
+                       $numlob = count($this->_refLOBs);
+               $this->_refLOBs[$numlob]['LOB'] = OCINewDescriptor($this->_connectionID, oci_lob_desc($type));
+                       $this->_refLOBs[$numlob]['TYPE'] = $isOutput;
+                       
+                       $tmp = $this->_refLOBs[$numlob]['LOB'];
+               $rez = OCIBindByName($stmt[1], ":".$name, $tmp, -1, $type);
+                       if ($this->debug) {
+                               ADOConnection::outp("<b>Bind</b>: descriptor has been allocated, var (".$name.") binded");
+                       }
+                       
+                       // if type is input then write data to lob now
+                       if ($isOutput == false) {
+                               $var = $this->BlobEncode($var);
+                               $tmp->WriteTemporary($var);
+                               $this->_refLOBs[$numlob]['VAR'] = $var;
+                               if ($this->debug) {
+                                       ADOConnection::outp("<b>Bind</b>: LOB has been written to temp");
+                               }
+                       } else {
+                               $this->_refLOBs[$numlob]['VAR'] = $var;
+                       }
+                       $rez = $tmp;
+               } else {
+                       if ($this->debug) 
+                               ADOConnection::outp("<b>Bind</b>: name = $name");
+                       
+                       if ($type !== false) $rez = OCIBindByName($stmt[1],":".$name,$var,$size,$type);
+                       else $rez = OCIBindByName($stmt[1],":".$name,$var,$size); // +1 byte for null terminator
+               }
+               
+               return $rez;
+       }
+       
+       function Param($name,$type=false)
+       {
+               return ':'.$name;
+       }
+       
+       /* 
+       Usage:
+               $stmt = $db->Prepare('select * from table where id =:myid and group=:group');
+               $db->Parameter($stmt,$id,'myid');
+               $db->Parameter($stmt,$group,'group');
+               $db->Execute($stmt);
+               
+               @param $stmt Statement returned by Prepare() or PrepareSP().
+               @param $var PHP variable to bind to
+               @param $name Name of stored procedure variable name to bind to.
+               @param [$isOutput] Indicates direction of parameter 0/false=IN  1=OUT  2= IN/OUT. This is ignored in oci8.
+               @param [$maxLen] Holds an maximum length of the variable.
+               @param [$type] The data type of $var. Legal values depend on driver.
+               
+               See OCIBindByName documentation at php.net.
+       */
+       function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
+       {
+                       if  ($this->debug) {
+                               $prefix = ($isOutput) ? 'Out' : 'In';
+                               $ztype = (empty($type)) ? 'false' : $type;
+                               ADOConnection::outp( "{$prefix}Parameter(\$stmt, \$php_var='$var', \$name='$name', \$maxLen=$maxLen, \$type=$ztype);");
+                       }
+                       return $this->Bind($stmt,$var,$maxLen,$type,$name,$isOutput);
+       }
+       
+       /*
+       returns query ID if successful, otherwise false
+       this version supports:
+       
+          1. $db->execute('select * from table');
+          
+          2. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
+                 $db->execute($prepared_statement, array(1,2,3));
+                 
+          3. $db->execute('insert into table (a,b,c) values (:a,:b,:c)',array('a'=>1,'b'=>2,'c'=>3));
+          
+          4. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
+                 $db->bind($stmt,1); $db->bind($stmt,2); $db->bind($stmt,3); 
+                 $db->execute($stmt);
+       */ 
+       function _query($sql,$inputarr)
+       {
+               if (is_array($sql)) { // is prepared sql
+                       $stmt = $sql[1];
+                       
+                       // we try to bind to permanent array, so that OCIBindByName is persistent
+                       // and carried out once only - note that max array element size is 4000 chars
+                       if (is_array($inputarr)) {
+                               $bindpos = $sql[3];
+                               if (isset($this->_bind[$bindpos])) {
+                               // all tied up already
+                                       $bindarr = $this->_bind[$bindpos];
+                               } else {
+                               // one statement to bind them all
+                                       $bindarr = array();
+                                       foreach($inputarr as $k => $v) {
+                                               $bindarr[$k] = $v;
+                                               OCIBindByName($stmt,":$k",$bindarr[$k],is_string($v) && strlen($v)>4000 ? -1 : 4000);
+                                       }
+                                       $this->_bind[$bindpos] = $bindarr;
+                               }
+                       }
+               } else {
+                       $stmt=OCIParse($this->_connectionID,$sql);
+               }
+                       
+               $this->_stmt = $stmt;
+               if (!$stmt) return false;
+       
+               if (defined('ADODB_PREFETCH_ROWS')) @OCISetPrefetch($stmt,ADODB_PREFETCH_ROWS);
+                       
+               if (is_array($inputarr)) {
+                       foreach($inputarr as $k => $v) {
+                               if (is_array($v)) {
+                                       if (sizeof($v) == 2) // suggested by g.giunta@libero.
+                                               OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]);
+                                       else
+                                               OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
+                                       
+                                       if ($this->debug==99) {
+                                               if (is_object($v[0])) 
+                                                       echo "name=:$k",' len='.$v[1],' type='.$v[2],'<br>';
+                                               else
+                                                       echo "name=:$k",' var='.$inputarr[$k][0],' len='.$v[1],' type='.$v[2],'<br>';
+                                               
+                                       }
+                               } else {
+                                       $len = -1;
+                                       if ($v === ' ') $len = 1;
+                                       if (isset($bindarr)) {  // is prepared sql, so no need to ocibindbyname again
+                                               $bindarr[$k] = $v;
+                                       } else {                                // dynamic sql, so rebind every time
+                                               OCIBindByName($stmt,":$k",$inputarr[$k],$len);
+                                       }
+                               }
+                       }
+               }
+               
+        $this->_errorMsg = false;
+               $this->_errorCode = false;
+               if (OCIExecute($stmt,$this->_commit)) {
+//OCIInternalDebug(1);                 
+                       if (count($this -> _refLOBs) > 0) {
+               
+                               foreach ($this -> _refLOBs as $key => $value) {
+                                       if ($this -> _refLOBs[$key]['TYPE'] == true) {
+                                               $tmp = $this -> _refLOBs[$key]['LOB'] -> load();
+                                               if ($this -> debug) {
+                                                       ADOConnection::outp("<b>OUT LOB</b>: LOB has been loaded. <br>");
+                                               }
+                                               //$_GLOBALS[$this -> _refLOBs[$key]['VAR']] = $tmp;
+                                               $this -> _refLOBs[$key]['VAR'] = $tmp;
+                                       } else {
+                        $this->_refLOBs[$key]['LOB']->save($this->_refLOBs[$key]['VAR']);
+                                               $this -> _refLOBs[$key]['LOB']->free();
+                                               unset($this -> _refLOBs[$key]);
+                        if ($this->debug) {
+                                                       ADOConnection::outp("<b>IN LOB</b>: LOB has been saved. <br>");
+                                               }
+                    }                                  
+                               }
+                       }
+               
+            switch (@OCIStatementType($stmt)) {
+                case "SELECT":
+                                       return $stmt;
+                               
+                               case 'DECLARE':
+                case "BEGIN":
+                    if (is_array($sql) && !empty($sql[4])) {
+                                               $cursor = $sql[4];
+                                               if (is_resource($cursor)) {
+                                                       $ok = OCIExecute($cursor);      
+                               return $cursor;
+                                               }
+                                               return $stmt;
+                    } else {
+                                               if (is_resource($stmt)) {
+                                                       OCIFreeStatement($stmt);
+                                                       return true;
+                                               }
+                        return $stmt;
+                    }
+                    break;
+                default :
+                                       // ociclose -- no because it could be used in a LOB?
+                    return true;
+            }
+               }
+               return false;
+       }
+       
+       // returns true or false
+       function _close()
+       {
+               if (!$this->_connectionID) return;
+               
+               if (!$this->autoCommit) OCIRollback($this->_connectionID);
+               if (count($this->_refLOBs) > 0) {
+                       foreach ($this ->_refLOBs as $key => $value) {
+                               $this->_refLOBs[$key]['LOB']->free();
+                               unset($this->_refLOBs[$key]);
+                       }
+               }
+               OCILogoff($this->_connectionID);
+               
+               $this->_stmt = false;
+               $this->_connectionID = false;
+       }
+       
+       function MetaPrimaryKeys($table, $owner=false,$internalKey=false)
+       {
+               if ($internalKey) return array('ROWID');
+               
+       // tested with oracle 8.1.7
+               $table = strtoupper($table);
+               if ($owner) {
+                       $owner_clause = "AND ((a.OWNER = b.OWNER) AND (a.OWNER = UPPER('$owner')))";
+                       $ptab = 'ALL_';
+               } else {
+                       $owner_clause = '';
+                       $ptab = 'USER_';
+               }
+               $sql = "
+SELECT /*+ RULE */ distinct b.column_name
+   FROM {$ptab}CONSTRAINTS a
+         , {$ptab}CONS_COLUMNS b
+  WHERE ( UPPER(b.table_name) = ('$table'))
+       AND (UPPER(a.table_name) = ('$table') and a.constraint_type = 'P')
+       $owner_clause
+       AND (a.constraint_name = b.constraint_name)";
+
+               $rs = $this->Execute($sql);
+               if ($rs && !$rs->EOF) {
+                       $arr = $rs->GetArray();
+                       $a = array();
+                       foreach($arr as $v) {
+                               $a[] = reset($v);
+                       }
+                       return $a;
+               }
+               else return false;
+       }
+       
+       // http://gis.mit.edu/classes/11.521/sqlnotes/referential_integrity.html
+       function MetaForeignKeys($table, $owner=false)
+       {
+       global $ADODB_FETCH_MODE;
+       
+               $save = $ADODB_FETCH_MODE;
+               $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+               $table = $this->qstr(strtoupper($table));
+               if (!$owner) {
+                       $owner = $this->user;
+                       $tabp = 'user_';
+               } else
+                       $tabp = 'all_';
+                       
+               $owner = ' and owner='.$this->qstr(strtoupper($owner));
+               
+               $sql = 
+"select constraint_name,r_owner,r_constraint_name 
+       from {$tabp}constraints
+       where constraint_type = 'R' and table_name = $table $owner";
+               
+               $constraints = $this->GetArray($sql);
+               $arr = false;
+               foreach($constraints as $constr) {
+                       $cons = $this->qstr($constr[0]);
+                       $rowner = $this->qstr($constr[1]);
+                       $rcons = $this->qstr($constr[2]);
+                       $cols = $this->GetArray("select column_name from {$tabp}cons_columns where constraint_name=$cons $owner order by position");
+                       $tabcol = $this->GetArray("select table_name,column_name from {$tabp}cons_columns where owner=$rowner and constraint_name=$rcons order by position");
+                       
+                       if ($cols && $tabcol) 
+                               for ($i=0, $max=sizeof($cols); $i < $max; $i++) {
+                                       $arr[$tabcol[$i][0]] = $cols[$i][0].'='.$tabcol[$i][1];
+                               }
+               }
+               $ADODB_FETCH_MODE = $save;
+               
+               return $arr;
+       }
+
+       
+       function CharMax()
+       {
+               return 4000;
+       }
+       
+       function TextMax()
+       {
+               return 4000;
+       }
+       
+       /**
+        * Quotes a string.
+        * An example is  $db->qstr("Don't bother",magic_quotes_runtime());
+        * 
+        * @param s                     the string to quote
+        * @param [magic_quotes]        if $s is GET/POST var, set to get_magic_quotes_gpc().
+        *                              This undoes the stupidity of magic quotes for GPC.
+        *
+        * @return  quoted string to be sent back to database
+        */
+       function qstr($s,$magic_quotes=false)
+       {       
+               //$nofixquotes=false;
+       
+               if ($this->noNullStrings && strlen($s)==0)$s = ' ';
+               if (!$magic_quotes) {   
+                       if ($this->replaceQuote[0] == '\\'){
+                               $s = str_replace('\\','\\\\',$s);
+                       }
+                       return  "'".str_replace("'",$this->replaceQuote,$s)."'";
+               }
+               
+               // undo magic quotes for "
+               $s = str_replace('\\"','"',$s);
+               
+               $s = str_replace('\\\\','\\',$s);
+               return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
+               
+       }
+       
+}
+
+/*--------------------------------------------------------------------------------------
+                Class Name: Recordset
+--------------------------------------------------------------------------------------*/
+
+class ADORecordset_oci8 extends ADORecordSet {
+
+       var $databaseType = 'oci8';
+       var $bind=false;
+       var $_fieldobjs;
+       
+       //var $_arr = false;
+               
+       function ADORecordset_oci8($queryID,$mode=false)
+       {
+               if ($mode === false) { 
+                       global $ADODB_FETCH_MODE;
+                       $mode = $ADODB_FETCH_MODE;
+               }
+               switch ($mode)
+               {
+               case ADODB_FETCH_ASSOC:$this->fetchMode = OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
+               case ADODB_FETCH_DEFAULT:
+               case ADODB_FETCH_BOTH:$this->fetchMode = OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
+               case ADODB_FETCH_NUM: 
+               default:
+               $this->fetchMode = OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
+               }
+               
+               $this->adodbFetchMode = $mode;
+               $this->_queryID = $queryID;
+       }
+
+
+       function Init()
+       {
+               if ($this->_inited) return;
+               
+               $this->_inited = true;
+               if ($this->_queryID) {
+                       
+                       $this->_currentRow = 0;
+                       @$this->_initrs();
+                       $this->EOF = !$this->_fetch();
+                       
+                       /*
+                       // based on idea by Gaetano Giunta to detect unusual oracle errors
+                       // see http://phplens.com/lens/lensforum/msgs.php?id=6771
+                       $err = OCIError($this->_queryID);
+                       if ($err && $this->connection->debug) ADOConnection::outp($err);
+                       */
+                       
+                       if (!is_array($this->fields)) {
+                               $this->_numOfRows = 0;
+                               $this->fields = array();
+                       }
+               } else {
+                       $this->fields = array();
+                       $this->_numOfRows = 0;
+                       $this->_numOfFields = 0;
+                       $this->EOF = true;
+               }
+       }
+       
+       function _initrs()
+       {
+               $this->_numOfRows = -1;
+               $this->_numOfFields = OCInumcols($this->_queryID);
+               if ($this->_numOfFields>0) {
+                       $this->_fieldobjs = array();
+                       $max = $this->_numOfFields;
+                       for ($i=0;$i<$max; $i++) $this->_fieldobjs[] = $this->_FetchField($i);
+               }
+       }
+
+         /*            Returns: an object containing field information.
+                         Get column information in the Recordset object. fetchField() can be used in order to obtain information about
+                         fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
+                         fetchField() is retrieved.            */
+
+       function _FetchField($fieldOffset = -1)
+       {
+               $fld = new ADOFieldObject;
+               $fieldOffset += 1;
+               $fld->name =OCIcolumnname($this->_queryID, $fieldOffset);
+               $fld->type = OCIcolumntype($this->_queryID, $fieldOffset);
+               $fld->max_length = OCIcolumnsize($this->_queryID, $fieldOffset);
+               switch($fld->type) {
+               case 'NUMBER':
+                       $p = OCIColumnPrecision($this->_queryID, $fieldOffset);
+                       $sc = OCIColumnScale($this->_queryID, $fieldOffset);
+                       if ($p != 0 && $sc == 0) $fld->type = 'INT';
+                       break;
+               
+               case 'CLOB':
+               case 'NCLOB':
+               case 'BLOB': 
+                       $fld->max_length = -1;
+                       break;
+               }
+               return $fld;
+       }
+       
+       /* For some reason, OCIcolumnname fails when called after _initrs() so we cache it */
+       function FetchField($fieldOffset = -1)
+       {
+               return $this->_fieldobjs[$fieldOffset];
+       }
+       
+       
+       /*
+       // 10% speedup to move MoveNext to child class
+       function _MoveNext() 
+       {
+       //global $ADODB_EXTENSION;if ($ADODB_EXTENSION) return @adodb_movenext($this);
+               
+               if ($this->EOF) return false;
+               
+               $this->_currentRow++;
+               if(@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode))
+                       return true;
+               $this->EOF = true;
+               
+               return false;
+       }       */
+       
+       
+       function MoveNext()
+       {
+               if (@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) {
+                       $this->_currentRow += 1;
+                       return true;
+               }
+               if (!$this->EOF) {
+                       $this->_currentRow += 1;
+                       $this->EOF = true;
+               }
+               return false;
+       }
+       
+       /*
+       # does not work as first record is retrieved in _initrs(), so is not included in GetArray()
+       function GetArray($nRows = -1) 
+       {
+       global $ADODB_OCI8_GETARRAY;
+       
+               if (true ||  !empty($ADODB_OCI8_GETARRAY)) {
+                       # does not support $ADODB_ANSI_PADDING_OFF
+       
+                       //OCI_RETURN_NULLS and OCI_RETURN_LOBS is set by OCIfetchstatement
+                       switch($this->adodbFetchMode) {
+                       case ADODB_FETCH_NUM:
+                       
+                               $ncols = @OCIfetchstatement($this->_queryID, $results, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW+OCI_NUM);
+                               $results = array_merge(array($this->fields),$results);
+                               return $results;
+                               
+                       case ADODB_FETCH_ASSOC: 
+                               if (ADODB_ASSOC_CASE != 2 || $this->databaseType != 'oci8') break;
+                               
+                               $ncols = @OCIfetchstatement($this->_queryID, $assoc, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW);
+                               $results = array_merge(array($this->fields),$assoc);
+                               return $results;
+                       
+                       default:
+                               break;
+                       }
+               }
+                       
+               $results = ADORecordSet::GetArray($nRows);
+               return $results;
+               
+       } */
+       
+       /* Optimize SelectLimit() by using OCIFetch() instead of OCIFetchInto() */
+       function GetArrayLimit($nrows,$offset=-1) 
+       {
+               if ($offset <= 0) {
+                       $arr = $this->GetArray($nrows);
+                       return $arr;
+               }
+               $arr = array();
+               for ($i=1; $i < $offset; $i++) 
+                       if (!@OCIFetch($this->_queryID)) return $arr;
+                       
+               if (!@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) return $arr;;
+               $results = array();
+               $cnt = 0;
+               while (!$this->EOF && $nrows != $cnt) {
+                       $results[$cnt++] = $this->fields;
+                       $this->MoveNext();
+               }
+               
+               return $results;
+       }
+
+       
+       /* Use associative array to get fields array */
+       function Fields($colname)
+       {
+               if (!$this->bind) {
+                       $this->bind = array();
+                       for ($i=0; $i < $this->_numOfFields; $i++) {
+                               $o = $this->FetchField($i);
+                               $this->bind[strtoupper($o->name)] = $i;
+                       }
+               }
+               
+                return $this->fields[$this->bind[strtoupper($colname)]];
+       }
+       
+
+
+       function _seek($row)
+       {
+               return false;
+       }
+
+       function _fetch() 
+       {
+               return @OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode);
+       }
+
+       /*              close() only needs to be called if you are worried about using too much memory while your script
+                       is running. All associated result memory for the specified result identifier will automatically be freed.               */
+
+       function _close() 
+       {
+               if ($this->connection->_stmt === $this->_queryID) $this->connection->_stmt = false;
+               if (!empty($this->_refcursor)) {
+                       OCIFreeCursor($this->_refcursor);
+                       $this->_refcursor = false;
+               }
+               @OCIFreeStatement($this->_queryID);
+               $this->_queryID = false;
+               
+       }
+
+       function MetaType($t,$len=-1)
+       {
+               if (is_object($t)) {
+                       $fieldobj = $t;
+                       $t = $fieldobj->type;
+                       $len = $fieldobj->max_length;
+               }
+               switch (strtoupper($t)) {
+               case 'VARCHAR':
+               case 'VARCHAR2':
+               case 'CHAR':
+               case 'VARBINARY':
+               case 'BINARY':
+               case 'NCHAR':
+               case 'NVARCHAR':
+               case 'NVARCHAR2':
+                                if (isset($this) && $len <= $this->blobSize) return 'C';
+               
+               case 'NCLOB':
+               case 'LONG':
+               case 'LONG VARCHAR':
+               case 'CLOB':
+               return 'X';
+               
+               case 'LONG RAW':
+               case 'LONG VARBINARY':
+               case 'BLOB':
+                       return 'B';
+               
+               case 'DATE': 
+                       return  ($this->connection->datetime) ? 'T' : 'D';
+               
+               
+               case 'TIMESTAMP': return 'T';
+               
+               case 'INT': 
+               case 'SMALLINT':
+               case 'INTEGER': 
+                       return 'I';
+                       
+               default: return 'N';
+               }
+       }
+}
+
+class ADORecordSet_ext_oci8 extends ADORecordSet_oci8 {        
+       function ADORecordSet_ext_oci8($queryID,$mode=false) 
+       {
+               if ($mode === false) { 
+                       global $ADODB_FETCH_MODE;
+                       $mode = $ADODB_FETCH_MODE;
+               }
+               switch ($mode)
+               {
+               case ADODB_FETCH_ASSOC:$this->fetchMode = OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
+               case ADODB_FETCH_DEFAULT:
+               case ADODB_FETCH_BOTH:$this->fetchMode = OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
+               case ADODB_FETCH_NUM: 
+               default: $this->fetchMode = OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
+               }
+               $this->adodbFetchMode = $mode;
+               $this->_queryID = $queryID;
+       }
+       
+       function MoveNext()
+       {
+               return adodb_movenext($this);
+       }
+}
+?>
index b1c75b148a428424d6b94aecf7ddb5f87ec07261..30518721eeb2519b305bc29051678717cb0c07cb 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /** 
- * @version V5.04a 25 Mar 2008  (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ * @version V5.06 16 Oct 2008  (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
index 33c7eeebc851a6140a2d11e56653f46bae6345a6..60a49a12ba93630bb6f918679b68a35a9c9c6997 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim. All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim. 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.
@@ -50,7 +50,7 @@ class ADODB_oci8po extends ADODB_oci8 {
        }
        
        // emulate handling of parameters ? ?, replacing with :bind0 :bind1
-       function _query($sql,$inputarr)
+       function _query($sql,$inputarr=false)
        {
                if (is_array($inputarr)) {
                        $i = 0;
@@ -102,7 +102,8 @@ class ADORecordset_oci8po extends ADORecordset_oci8 {
        {
                 $fld = new ADOFieldObject;
                 $fieldOffset += 1;
-                $fld->name = strtolower(OCIcolumnname($this->_queryID, $fieldOffset));
+                $fld->name = OCIcolumnname($this->_queryID, $fieldOffset);
+                if (ADODB_ASSOC_CASE == 0) $fld->name = strtolower($fld->name);
                 $fld->type = OCIcolumntype($this->_queryID, $fieldOffset);
                 $fld->max_length = OCIcolumnsize($this->_queryID, $fieldOffset);
                 if ($fld->type == 'NUMBER') {
index 38099920ac927da8e96d08c1dee87d3877add14d..c799c00b015fb97f3463736b050199bdf6b6d574 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
index 3a3449ff3b42c5183a12755aa6335206988aee2c..ce85376e94a93fe24ce490670ddec217ef071f7c 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
index 5754ee0151082f9b66c3a0316e86f4bf951d616a..97f4cd02ef2ec7255d6b9f084bccf35465739ad9 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
@@ -177,7 +177,7 @@ order by constraint_name, referenced_table_name, keyno";
         return $indexes;
        }
        
-       function _query($sql,$inputarr)
+       function _query($sql,$inputarr=false)
        {
                if (is_string($sql)) $sql = str_replace('||','+',$sql);
                return ADODB_odbc::_query($sql,$inputarr);
index 6f2bd5b28396f5ea335a2bd161a25e974d59ff51..356082654f5b1dc3c5b0ff98f2d7f9b93ee506ac 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
index e9fe830feaac9589e1c8c931d25340640705156d..e9c7d72ede087cdb52b8b54faf0677ffc7a4cfe2 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
-  V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -45,16 +45,39 @@ class ADODB_odbtp extends ADOConnection{
 
        function ErrorMsg()
        {
+               if ($this->_errorMsg !== false) return $this->_errorMsg;
                if (empty($this->_connectionID)) return @odbtp_last_error();
                return @odbtp_last_error($this->_connectionID);
        }
 
        function ErrorNo()
        {
+               if ($this->_errorCode !== false) return $this->_errorCode;
                if (empty($this->_connectionID)) return @odbtp_last_error_state();
                        return @odbtp_last_error_state($this->_connectionID);
        }
-
+/*
+       function DBDate($d,$isfld=false)
+       {
+               if (empty($d) && $d !== 0) return 'null';
+               if ($isfld) return "convert(date, $d, 120)";
+               
+               if (is_string($d)) $d = ADORecordSet::UnixDate($d);
+               $d = adodb_date($this->fmtDate,$d);
+               return "convert(date, $d, 120)"; 
+       }
+       
+       function DBTimeStamp($d,$isfld=false)
+       {
+               if (empty($d) && $d !== 0) return 'null';
+               if ($isfld) return "convert(datetime, $d, 120)";
+               
+               if (is_string($d)) $d = ADORecordSet::UnixDate($d);
+               $d = adodb_date($this->fmtDate,$d);
+               return "convert(datetime, $d, 120)"; 
+       }
+*/
+       
        function _insertid()
        {
        // SCOPE_IDENTITY()
@@ -437,6 +460,10 @@ class ADODB_odbtp extends ADOConnection{
        function Prepare($sql)
        {
                if (! $this->_bindInputArray) return $sql; // no binding
+               
+        $this->_errorMsg = false;
+               $this->_errorCode = false;
+               
                $stmt = @odbtp_prepare($sql,$this->_connectionID);
                if (!$stmt) {
                //      print "Prepare Error for ($sql) ".$this->ErrorMsg()."<br>";
@@ -449,6 +476,9 @@ class ADODB_odbtp extends ADOConnection{
        {
                if (!$this->_canPrepareSP) return $sql; // Can't prepare procedures
 
+        $this->_errorMsg = false;
+               $this->_errorCode = false;
+               
                $stmt = @odbtp_prepare_proc($sql,$this->_connectionID);
                if (!$stmt) return false;
                return array($sql,$stmt);
@@ -511,6 +541,56 @@ class ADODB_odbtp extends ADOConnection{
                return @odbtp_execute( $stmt ) != false;
        }
 
+       function MetaIndexes($table,$primary=false)
+       {
+               switch ( $this->odbc_driver) {
+                       case ODB_DRIVER_MSSQL:
+                               return $this->MetaIndexes_mssql($table, $primary);
+                       default:
+                               return array();
+               }
+       }
+       
+       function MetaIndexes_mssql($table,$primary=false)
+       {
+               $table = strtolower($this->qstr($table));
+
+               $sql = "SELECT i.name AS ind_name, C.name AS col_name, USER_NAME(O.uid) AS Owner, c.colid, k.Keyno, 
+                       CASE WHEN I.indid BETWEEN 1 AND 254 AND (I.status & 2048 = 2048 OR I.Status = 16402 AND O.XType = 'V') THEN 1 ELSE 0 END AS IsPK,
+                       CASE WHEN I.status & 2 = 2 THEN 1 ELSE 0 END AS IsUnique
+                       FROM dbo.sysobjects o INNER JOIN dbo.sysindexes I ON o.id = i.id 
+                       INNER JOIN dbo.sysindexkeys K ON I.id = K.id AND I.Indid = K.Indid 
+                       INNER JOIN dbo.syscolumns c ON K.id = C.id AND K.colid = C.Colid
+                       WHERE LEFT(i.name, 8) <> '_WA_Sys_' AND o.status >= 0 AND lower(O.Name) = $table
+                       ORDER BY O.name, I.Name, K.keyno";
+
+               global $ADODB_FETCH_MODE;
+               $save = $ADODB_FETCH_MODE;
+        $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+        if ($this->fetchMode !== FALSE) {
+               $savem = $this->SetFetchMode(FALSE);
+        }
+        
+        $rs = $this->Execute($sql);
+        if (isset($savem)) {
+               $this->SetFetchMode($savem);
+        }
+        $ADODB_FETCH_MODE = $save;
+
+        if (!is_object($rs)) {
+               return FALSE;
+        }
+
+               $indexes = array();
+               while ($row = $rs->FetchRow()) {
+                       if ($primary && !$row[5]) continue;
+                       
+            $indexes[$row[0]]['unique'] = $row[6];
+            $indexes[$row[0]]['columns'][] = $row[1];
+       }
+        return $indexes;
+       }
+       
        function IfNull( $field, $ifNull )
        {
                switch( $this->odbc_driver ) {
@@ -526,6 +606,9 @@ class ADODB_odbtp extends ADOConnection{
        {
        global $php_errormsg;
        
+        $this->_errorMsg = false;
+               $this->_errorCode = false;
+               
                if ($inputarr) {
                        if (is_array($sql)) {
                                $stmtid = $sql[1];
@@ -537,10 +620,20 @@ class ADODB_odbtp extends ADOConnection{
                                }
                        }
                        $num_params = @odbtp_num_params( $stmtid );
+                       /*
                        for( $param = 1; $param <= $num_params; $param++ ) {
                                @odbtp_input( $stmtid, $param );
                                @odbtp_set( $stmtid, $param, $inputarr[$param-1] );
+                       }*/
+                       
+                       $param = 1;
+                       foreach($inputarr as $v) {
+                               @odbtp_input( $stmtid, $param );
+                               @odbtp_set( $stmtid, $param, $v );
+                               $param += 1;
+                               if ($param > $num_params) break;
                        }
+                       
                        if (!@odbtp_execute($stmtid) ) {
                                return false;
                        }
index 5af24c0f97f122c9a8ca378c72362f6a31a68be9..ad26c528bd4b721716e387b22d55503043e0ffb9 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
-       V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+       V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
index addc549207346b976e46c8f4cf74569c5df78d65..6e926c6363e043dba08f7f5cfca67870fedb6e43 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
index f7ee6d0e9a1089480d701073b72fcaf19c09e5b6..cdbe617edb8130981fb23656490136a1c27bb786 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
@@ -158,6 +158,7 @@ class ADODB_pdo extends ADOConnection {
                        case 'mysql':
                        case 'pgsql':
                        case 'mssql':
+                       case 'sqlite':
                                include_once(ADODB_DIR.'/drivers/adodb-pdo_'.$this->dsnType.'.inc.php');
                                break;
                        }
@@ -174,6 +175,15 @@ class ADODB_pdo extends ADOConnection {
                return false;
        }
        
+       function Concat() 
+       {
+               $args = func_get_args();
+               if(method_exists($this->_driver, 'Concat')) 
+                       return call_user_func_array(array($this->_driver, 'Concat'), $args); 
+               
+               return call_user_func_array(array($this,'parent::Concat'), $args); 
+       }
+       
        // returns true or false
        function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename)
        {
@@ -216,6 +226,10 @@ class ADODB_pdo extends ADOConnection {
                else $obj->bindParam($name, $var);
        }
        
+       function OffsetDate($dayFraction,$date=false)
+    {   
+        return $this->_driver->OffsetDate($dayFraction,$date);
+    }
        
        function ErrorMsg()
        {
@@ -248,8 +262,19 @@ class ADODB_pdo extends ADOConnection {
                return $err;
        }
 
+       function SetTransactionMode($transaction_mode) 
+       {
+               if(method_exists($this->_driver, 'SetTransactionMode')) 
+                       return $this->_driver->SetTransactionMode($transaction_mode); 
+               
+               return parent::SetTransactionMode($seqname); 
+       }
+
        function BeginTrans()
        {       
+               if(method_exists($this->_driver, 'BeginTrans')) 
+                       return $this->_driver->BeginTrans(); 
+               
                if (!$this->hasTransactions) return false;
                if ($this->transOff) return true; 
                $this->transCnt += 1;
@@ -260,6 +285,9 @@ class ADODB_pdo extends ADOConnection {
        
        function CommitTrans($ok=true) 
        { 
+               if(method_exists($this->_driver, 'CommitTrans')) 
+                       return $this->_driver->CommitTrans($ok); 
+               
                if (!$this->hasTransactions) return false;
                if ($this->transOff) return true; 
                if (!$ok) return $this->RollbackTrans();
@@ -273,6 +301,9 @@ class ADODB_pdo extends ADOConnection {
        
        function RollbackTrans()
        {
+               if(method_exists($this->_driver, 'RollbackTrans')) 
+                       return $this->_driver->RollbackTrans(); 
+               
                if (!$this->hasTransactions) return false;
                if ($this->transOff) return true; 
                if ($this->transCnt) $this->transCnt -= 1;
@@ -299,6 +330,30 @@ class ADODB_pdo extends ADOConnection {
                return $obj;
        }
        
+       function CreateSequence($seqname='adodbseq',$startID=1)
+       {
+               if(method_exists($this->_driver, 'CreateSequence')) 
+                       return $this->_driver->CreateSequence($seqname, $startID); 
+               
+               return parent::CreateSequence($seqname, $startID); 
+       }
+       
+       function DropSequence($seqname='adodbseq')
+       {
+               if(method_exists($this->_driver, 'DropSequence')) 
+                       return $this->_driver->DropSequence($seqname); 
+               
+               return parent::DropSequence($seqname); 
+       }
+
+       function GenID($seqname='adodbseq',$startID=1)
+       {
+               if(method_exists($this->_driver, 'GenID')) 
+                       return $this->_driver->GenID($seqname, $startID); 
+               
+               return parent::GenID($seqname, $startID); 
+       }
+
        
        /* returns queryID or false */
        function _query($sql,$inputarr=false) 
index a6830942720ae67cabcd6c4989cb070cc1f138d8..270b57cc0838d423e4c6b9f33b8f4244a96c2280 100644 (file)
@@ -2,7 +2,7 @@
 
 
 /*
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -47,12 +47,12 @@ class ADODB_pdo_mssql extends ADODB_pdo {
                $this->Execute("SET TRANSACTION ".$transaction_mode);
        }
        
-       function MetaTables()
+       function MetaTables($ttype=false,$showSchema=false,$mask=false) 
        {
                return false;
        }
        
-       function MetaColumns()
+       function MetaColumns($table,$normalize=true)
        {
                return false;
        }
index 70b73b0174f4930781aa5c5d01053488e12fa21a..b6498e437a58b814b7eac0c8d67b27ec7bdda17c 100644 (file)
@@ -2,7 +2,7 @@
 
 
 /*
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -18,14 +18,14 @@ class ADODB_pdo_mysql extends ADODB_pdo {
        var $hasGenID = true;
        var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);";
        var $_dropSeqSQL = "drop table %s";
-
+       var $fmtTimeStamp = "'Y-m-d, H:i:s'";
        var $nameQuote = '`';
 
        function _init($parentDriver)
        {
        
                $parentDriver->hasTransactions = false;
-               $parentDriver->_bindInputArray = false;
+               #$parentDriver->_bindInputArray = false;
                $parentDriver->hasInsertID = true;
                $parentDriver->_connectionID->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY,true);
        }
@@ -41,6 +41,16 @@ class ADODB_pdo_mysql extends ADODB_pdo {
 //             return "from_unixtime(unix_timestamp($date)+$fraction)";
        }
        
+       function Concat() 
+       {       
+               $s = "";
+               $arr = func_get_args();
+
+               // suggestion by andrew005#mnogo.ru
+               $s = implode(',',$arr);
+               if (strlen($s) > 0) return "CONCAT($s)"; return ''; 
+       }
+       
        function ServerInfo()
        {
                $arr['description'] = ADOConnection::GetOne("select version()");
@@ -76,7 +86,7 @@ class ADODB_pdo_mysql extends ADODB_pdo {
                $this->Execute("SET SESSION TRANSACTION ".$transaction_mode);
        }
        
-       function MetaColumns($table
+       function MetaColumns($table,$normalize=true)
        {
                $this->_findschema($table,$schema);
                if ($schema) {
index b5f5868a4aa6941d21372fabb270b73d7580fd13..1f58ea8ed1111e23229dd314394a08d9499a19bf 100644 (file)
@@ -2,7 +2,7 @@
 
 
 /*
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -47,7 +47,7 @@ class ADODB_pdo_oci extends ADODB_pdo_base {
                return $ret;
        }
        
-       function MetaColumns($table
+       function MetaColumns($table,$normalize=true)
        {
        global $ADODB_FETCH_MODE;
        
index 1bf137f8c3bb4567d573b5e71884e5967e2b963b..f8443bcdc7b6827aba4b884d60c415c02531bd5b 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /*
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
diff --git a/lib/adodb/drivers/adodb-pdo_sqlite.inc.php b/lib/adodb/drivers/adodb-pdo_sqlite.inc.php
new file mode 100644 (file)
index 0000000..5956ac5
--- /dev/null
@@ -0,0 +1,190 @@
+<?php
+
+/* 
+ V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
+  
+  Latest version is available at http://adodb.sourceforge.net
+  
+  Thanks Diogo Toscano (diogo#scriptcase.net) for the code.
+       And also Sid Dunayer [sdunayer#interserv.com] for extensive fixes.
+*/
+
+class ADODB_pdo_sqlite extends ADODB_pdo {
+       var $metaTablesSQL   = "SELECT name FROM sqlite_master WHERE type='table'";
+       var $sysDate         = 'current_date';
+       var $sysTimeStamp    = 'current_timestamp';
+       var $nameQuote       = '`';
+       var $replaceQuote    = "''";
+       var $hasGenID        = true;
+       var $_genIDSQL       = "UPDATE %s SET id=id+1 WHERE id=%s";
+       var $_genSeqSQL      = "CREATE TABLE %s (id integer)";
+       var $_genSeqCountSQL = 'SELECT COUNT(*) FROM %s';
+       var $_genSeq2SQL     = 'INSERT INTO %s VALUES(%s)';
+       var $_dropSeqSQL     = 'DROP TABLE %s';
+       var $concat_operator = '||';
+    var $pdoDriver       = false;
+    
+       function _init($parentDriver)
+       {
+               $this->pdoDriver = $parentDriver;
+               $parentDriver->_bindInputArray = true;
+               $parentDriver->hasTransactions = true;
+               $parentDriver->hasInsertID = true;
+       }
+
+       function ServerInfo()
+       {
+               $parent = $this->pdoDriver;
+               @($ver = array_pop($parent->GetCol("SELECT sqlite_version()")));
+               @($end = array_pop($parent->GetCol("PRAGMA encoding")));
+
+               $arr['version']     = $ver;
+               $arr['description'] = 'SQLite ';
+               $arr['encoding']    = $enc;
+
+               return $arr;
+       }
+       
+       function SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs2cache=0) 
+       {
+               $parent = $this->pdoDriver;
+               $offsetStr = ($offset >= 0) ? " OFFSET $offset" : '';
+               $limitStr  = ($nrows >= 0)  ? " LIMIT $nrows" : ($offset >= 0 ? ' LIMIT 999999999' : '');
+               if ($secs2cache)
+                       $rs = $parent->CacheExecute($secs2cache,$sql."$limitStr$offsetStr",$inputarr);
+               else
+                       $rs = $parent->Execute($sql."$limitStr$offsetStr",$inputarr);
+
+               return $rs;
+       }
+
+       function GenID($seq='adodbseq',$start=1)
+       {
+               $parent = $this->pdoDriver;
+               // if you have to modify the parameter below, your database is overloaded,
+               // or you need to implement generation of id's yourself!
+               $MAXLOOPS = 100;
+               while (--$MAXLOOPS>=0) {
+                       @($num = array_pop($parent->GetCol("SELECT id FROM {$seq}")));
+                       if ($num === false || !is_numeric($num)) {
+                               @$parent->Execute(sprintf($this->_genSeqSQL ,$seq));
+                               $start -= 1;
+                               $num = '0';
+                               $cnt = $parent->GetOne(sprintf($this->_genSeqCountSQL,$seq));
+                               if (!$cnt) {
+                                       $ok = $parent->Execute(sprintf($this->_genSeq2SQL,$seq,$start));
+                               }
+                               if (!$ok) return false;
+                       }
+                       $parent->Execute(sprintf($this->_genIDSQL,$seq,$num));
+
+                       if ($parent->affected_rows() > 0) {
+                               $num += 1;
+                               $parent->genID = intval($num);
+                               return intval($num);
+                       }
+               }
+               if ($fn = $parent->raiseErrorFn) {
+                       $fn($parent->databaseType,'GENID',-32000,"Unable to generate unique id after $MAXLOOPS attempts",$seq,$num);
+               }
+               return false;
+       }
+
+       function CreateSequence($seqname='adodbseq',$start=1)
+       {
+               $parent = $this->pdoDriver;
+               $ok = $parent->Execute(sprintf($this->_genSeqSQL,$seqname));
+               if (!$ok) return false;
+               $start -= 1;
+               return $parent->Execute("insert into $seqname values($start)");
+       }
+
+       function SetTransactionMode($transaction_mode)
+       {
+               $parent = $this->pdoDriver;
+               $parent->_transmode = strtoupper($transaction_mode);
+       }
+
+       function BeginTrans()
+       {       
+               $parent = $this->pdoDriver;
+               if ($parent->transOff) return true; 
+               $parent->transCnt += 1;
+               $parent->_autocommit = false;
+               return $parent->Execute("BEGIN {$parent->_transmode}");
+       }
+       
+       function CommitTrans($ok=true) 
+       { 
+               $parent = $this->pdoDriver;
+               if ($parent->transOff) return true; 
+               if (!$ok) return $parent->RollbackTrans();
+               if ($parent->transCnt) $parent->transCnt -= 1;
+               $parent->_autocommit = true;
+               
+               $ret = $parent->Execute('COMMIT');
+               return $ret;
+       }
+       
+       function RollbackTrans()
+       {
+               $parent = $this->pdoDriver;
+               if ($parent->transOff) return true; 
+               if ($parent->transCnt) $parent->transCnt -= 1;
+               $parent->_autocommit = true;
+               
+               $ret = $parent->Execute('ROLLBACK');
+               return $ret;
+       }
+
+
+    // mark newnham
+       function MetaColumns($tab,$normalize=true))
+       {
+         global $ADODB_FETCH_MODE;
+
+         $parent = $this->pdoDriver;
+         $false = false;
+         $save = $ADODB_FETCH_MODE;
+         $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
+         if ($parent->fetchMode !== false) $savem = $parent->SetFetchMode(false);
+         $rs = $parent->Execute("PRAGMA table_info('$tab')");
+         if (isset($savem)) $parent->SetFetchMode($savem);
+         if (!$rs) {
+           $ADODB_FETCH_MODE = $save; 
+           return $false;
+         }
+         $arr = array();
+         while ($r = $rs->FetchRow()) {
+           $type = explode('(',$r['type']);
+           $size = '';
+           if (sizeof($type)==2)
+           $size = trim($type[1],')');
+           $fn = strtoupper($r['name']);
+           $fld = new ADOFieldObject;
+           $fld->name = $r['name'];
+           $fld->type = $type[0];
+           $fld->max_length = $size;
+           $fld->not_null = $r['notnull'];
+           $fld->primary_key = $r['pk'];
+           $fld->default_value = $r['dflt_value'];
+           $fld->scale = 0;
+           if ($save == ADODB_FETCH_NUM) $arr[] = $fld;        
+           else $arr[strtoupper($fld->name)] = $fld;
+         }
+         $rs->Close();
+         $ADODB_FETCH_MODE = $save;
+         return $arr;
+       }
+
+       function MetaTables($ttype=false,$showSchema=false,$mask=false)
+       {
+               $parent = $this->pdoDriver;
+               return $parent->GetCol($this->metaTablesSQL);
+       }
+}
+?>
\ No newline at end of file
index 916ad98eca34ec12436117d5c324214b46d539e6..ffd885b33e771fab295c0f0c57007d28565f556b 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
- V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
index c0e8c76efadbf012ce2e30c409e8493def60439a..07e3e6ab6c892fbaeab993eecd2131ae5d3143ae 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
- V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -236,6 +236,8 @@ select viewname,'V' from pg_views where viewname like $mask";
        // if magic quotes disabled, use pg_escape_string()
        function qstr($s,$magic_quotes=false)
        {
+               if (is_bool($s)) return $s ? 'true' : 'false';
+                
                if (!$magic_quotes) {
                        if (ADODB_PHPVER >= 0x5200) {
                                return  "'".pg_escape_string($this->_connectionID,$s)."'";
@@ -713,7 +715,7 @@ WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\'))';
        
 
        // returns queryID or false
-       function _query($sql,$inputarr)
+       function _query($sql,$inputarr=false)
        {
                $this->_errorMsg = false;
                if ($inputarr) {
@@ -943,6 +945,7 @@ class ADORecordSet_postgres64 extends ADORecordSet{
        
        function _decode($blob)
        {
+               if ($blob === NULL) return NULL;
                eval('$realblob="'.adodb_str_replace(array('"','$'),array('\"','\$'),$blob).'";');
                return $realblob;       
        }
index ded804ec5093001a8292c9cb9042174b7b88360a..63fc9a14c34182aed6785e1d4ce0b4ce5a1355fc 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
- V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
@@ -56,9 +56,58 @@ class ADODB_postgres7 extends ADODB_postgres64 {
        }
        */
 
-
-       // from  Edward Jaramilla, improved version - works on pg 7.4
+       /*
+               I discovered that the MetaForeignKeys method no longer worked for Postgres 8.3.
+               I went ahead and modified it to work for both 8.2 and 8.3. 
+               Please feel free to include this change in your next release of adodb.
+                William Kolodny [William.Kolodny#gt-t.net]
+       */
        function MetaForeignKeys($table, $owner=false, $upper=false)
+       {
+         $sql="
+         SELECT fum.ftblname AS lookup_table, split_part(fum.rf, ')'::text, 1) AS lookup_field,
+            fum.ltable AS dep_table, split_part(fum.lf, ')'::text, 1) AS dep_field
+         FROM (
+         SELECT fee.ltable, fee.ftblname, fee.consrc, split_part(fee.consrc,'('::text, 2) AS lf, 
+           split_part(fee.consrc, '('::text, 3) AS rf
+         FROM (
+             SELECT foo.relname AS ltable, foo.ftblname,
+                 pg_get_constraintdef(foo.oid) AS consrc
+             FROM (
+                 SELECT c.oid, c.conname AS name, t.relname, ft.relname AS ftblname
+                 FROM pg_constraint c 
+                 JOIN pg_class t ON (t.oid = c.conrelid) 
+                 JOIN pg_class ft ON (ft.oid = c.confrelid)
+                 JOIN pg_namespace nft ON (nft.oid = ft.relnamespace)
+                 LEFT JOIN pg_description ds ON (ds.objoid = c.oid)
+                 JOIN pg_namespace n ON (n.oid = t.relnamespace)
+                 WHERE c.contype = 'f'::\"char\"
+                 ORDER BY t.relname, n.nspname, c.conname, c.oid
+                 ) foo
+             ) fee) fum
+         WHERE fum.ltable='".strtolower($table)."'
+         ORDER BY fum.ftblname, fum.ltable, split_part(fum.lf, ')'::text, 1)
+         ";
+         $rs = $this->Execute($sql);
+       
+         if (!$rs || $rs->EOF) return false;
+       
+         $a = array();
+         while (!$rs->EOF) {
+           if ($upper) {
+             $a[strtoupper($rs->Fields('lookup_table'))][] = strtoupper(str_replace('"','',$rs->Fields('dep_field').'='.$rs->Fields('lookup_field')));
+           } else {
+             $a[$rs->Fields('lookup_table')][] = str_replace('"','',$rs->Fields('dep_field').'='.$rs->Fields('lookup_field'));
+           }
+               $rs->MoveNext();
+         }
+       
+         return $a;
+       
+       }
+       
+       // from  Edward Jaramilla, improved version - works on pg 7.4
+       function _old_MetaForeignKeys($table, $owner=false, $upper=false)
        {
                $sql = 'SELECT t.tgargs as args
                FROM
@@ -91,7 +140,7 @@ class ADODB_postgres7 extends ADODB_postgres64 {
                return $a;
        }
 
-       function _query($sql,$inputarr)
+       function _query($sql,$inputarr=false)
        {
                if (! $this->_bindInputArray) {
                        // We don't have native support for parameterized queries, so let's emulate it at the parent
index 08267ecc9d85d0061a1eeb9895f77d6e9f0af69b..f4bab5c6dc367744910335fed55d911fd3adf919 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
- V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
index 581d83fc49bebf1f9fdf801ed1b2fffcd0c240d1..5e2effdcc2550daca7ba8da199f1bf89ad3a4faf 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
index 41ad9cc42d837c8e4ed1ef36f2d8ad2f4262bcc1..5d29793995a755da890bc444fd96400e51ca5ac1 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
index dbb6e98db6bb199d00946ea9b477eb0b19da281e..22293230892a6ad4e5254f341828c24720b78b08 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-version V5.04a 25 Mar 2008  (c) 2000-2008  John Lim (jlim#natsoft.com.my).  All rights
+version V5.06 16 Oct 2008  (c) 2000-2009  John Lim (jlim#natsoft.com).  All rights
 reserved.
   Released under both BSD license and Lesser GPL library license. 
   Whenever there is any discrepancy between the two licenses, 
index 845b92f535115be331c14f5fb68617c7669002a5..ff45107d173c39e0ca45c454858768cbaa7b7ff5 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
index 2555eeae19580e7ed54c4a604cef2370e3add923..a4daab7cc47062ac6f8f39ec83066afdc3db64d8 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
index d8ba4009537974291825c967c2086a4064f7814f..eb417ba765d85be45e575764d9a87b4404993c1d 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim. All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim. 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. 
@@ -123,6 +123,12 @@ class ADODB_sybase extends ADOConnection {
        {
                if (!function_exists('sybase_connect')) return null;
                
+               if ($this->charSet) {
+                       $this->_connectionID = sybase_connect($argHostname,$argUsername,$argPassword, $this->charSet);
+               } else {
+                       $this->_connectionID = sybase_connect($argHostname,$argUsername,$argPassword);
+               }
+
                $this->_connectionID = sybase_connect($argHostname,$argUsername,$argPassword);
                if ($this->_connectionID === false) return false;
                if ($argDatabasename) return $this->SelectDB($argDatabasename);
@@ -133,14 +139,18 @@ class ADODB_sybase extends ADOConnection {
        {
                if (!function_exists('sybase_connect')) return null;
                
-               $this->_connectionID = sybase_pconnect($argHostname,$argUsername,$argPassword);
+               if ($this->charSet) {
+                       $this->_connectionID = sybase_pconnect($argHostname,$argUsername,$argPassword, $this->charSet);
+               } else {
+                       $this->_connectionID = sybase_pconnect($argHostname,$argUsername,$argPassword);
+               }
                if ($this->_connectionID === false) return false;
                if ($argDatabasename) return $this->SelectDB($argDatabasename);
                return true;    
        }
        
        // returns query ID if successful, otherwise false
-       function _query($sql,$inputarr)
+       function _query($sql,$inputarr=false)
        {
        global $ADODB_COUNTRECS;
        
@@ -177,12 +187,12 @@ class ADODB_sybase extends ADOConnection {
                return @sybase_close($this->_connectionID);
        }
        
-       function UnixDate($v)
+       static function UnixDate($v)
        {
                return ADORecordSet_array_sybase::UnixDate($v);
        }
        
-       function UnixTimeStamp($v)
+       static function UnixTimeStamp($v)
        {
                return ADORecordSet_array_sybase::UnixTimeStamp($v);
        }       
@@ -211,7 +221,7 @@ class ADODB_sybase extends ADOConnection {
                 $s .= "convert(char(3),$col,0)";
                 break;
             case 'm':
-                $s .= "replace(str(month($col),2),' ','0')";
+                $s .= "str_replace(str(month($col),2),' ','0')";
                 break;
             case 'Q':
             case 'q':
@@ -219,21 +229,21 @@ class ADODB_sybase extends ADOConnection {
                 break;
             case 'D':
             case 'd':
-                $s .= "replace(str(datepart(dd,$col),2),' ','0')";
+                $s .= "str_replace(str(datepart(dd,$col),2),' ','0')";
                 break;
             case 'h':
                 $s .= "substring(convert(char(14),$col,0),13,2)";
                 break;
 
             case 'H':
-                $s .= "replace(str(datepart(hh,$col),2),' ','0')";
+                $s .= "str_replace(str(datepart(hh,$col),2),' ','0')";
                 break;
 
             case 'i':
-                $s .= "replace(str(datepart(mi,$col),2),' ','0')";
+                $s .= "str_replace(str(datepart(mi,$col),2),' ','0')";
                 break;
             case 's':
-                $s .= "replace(str(datepart(ss,$col),2),' ','0')";
+                $s .= "str_replace(str(datepart(ss,$col),2),' ','0')";
                 break;
             case 'a':
             case 'A':
@@ -353,12 +363,12 @@ class ADORecordset_sybase extends ADORecordSet {
        }
        
        // sybase/mssql uses a default date like Dec 30 2000 12:00AM
-       function UnixDate($v)
+       static function UnixDate($v)
        {
                return ADORecordSet_array_sybase::UnixDate($v);
        }
        
-       function UnixTimeStamp($v)
+       static function UnixTimeStamp($v)
        {
                return ADORecordSet_array_sybase::UnixTimeStamp($v);
        }
@@ -371,12 +381,12 @@ class ADORecordSet_array_sybase extends ADORecordSet_array {
        }
        
                // sybase/mssql uses a default date like Dec 30 2000 12:00AM
-       function UnixDate($v)
+       static function UnixDate($v)
        {
        global $ADODB_sybase_mths;
        
                //Dec 30 2000 12:00AM
-               if (!ereg( "([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4})"
+               if (!preg_match( "/([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4})/"
                        ,$v, $rr)) return parent::UnixDate($v);
                        
                if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
@@ -388,12 +398,12 @@ class ADORecordSet_array_sybase extends ADORecordSet_array {
                return  mktime(0,0,0,$themth,$rr[2],$rr[3]);
        }
        
-       function UnixTimeStamp($v)
+       static function UnixTimeStamp($v)
        {
        global $ADODB_sybase_mths;
                //11.02.2001 Toni Tunkkari toni.tunkkari@finebyte.com
                //Changed [0-9] to [0-9 ] in day conversion
-               if (!ereg( "([A-Za-z]{3})[-/\. ]([0-9 ]{1,2})[-/\. ]([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})"
+               if (!preg_match( "/([A-Za-z]{3})[-/\. ]([0-9 ]{1,2})[-/\. ]([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})/"
                        ,$v, $rr)) return parent::UnixTimeStamp($v);
                if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
                
index 5360c2a46c32a907d09b3d322cf199c99567cb04..ca6db057a5d4a046ae7bc9820f9bd84f179daaaf 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
-  V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
index 3b13aef61d80306a9f0261c54d82990484e54ba3..97636c9ac1c3a3e69769a09abc245a84aaf07a64 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
diff --git a/lib/adodb/lang/adodb-fa.inc.php b/lib/adodb/lang/adodb-fa.inc.php
new file mode 100644 (file)
index 0000000..5594313
--- /dev/null
@@ -0,0 +1,35 @@
+<?php\r
+\r
+/* Farsi - by "Peyman Hooshmandi Raad" <phooshmand#gmail.com> */\r
+\r
+$ADODB_LANG_ARRAY = array (\r
+                       'LANG'                      => 'fa',\r
+            DB_ERROR                    => 'خطای ناشناخته',\r
+            DB_ERROR_ALREADY_EXISTS     => 'وجود دارد',\r
+            DB_ERROR_CANNOT_CREATE      => 'امکان create وجود ندارد',\r
+            DB_ERROR_CANNOT_DELETE      => 'امکان حذف وجود ندارد',\r
+            DB_ERROR_CANNOT_DROP        => 'امکان drop وجود ندارد',\r
+            DB_ERROR_CONSTRAINT         => 'نقض شرط',\r
+            DB_ERROR_DIVZERO            => 'تقسیم بر صفر',\r
+            DB_ERROR_INVALID            => 'نامعتبر',\r
+            DB_ERROR_INVALID_DATE       => 'زمان یا تاریخ نامعتبر',\r
+            DB_ERROR_INVALID_NUMBER     => 'عدد نامعتبر',\r
+            DB_ERROR_MISMATCH           => 'عدم مطابقت',\r
+            DB_ERROR_NODBSELECTED       => 'بانک اطلاعاتی انتخاب نشده است',\r
+            DB_ERROR_NOSUCHFIELD        => 'چنین ستونی وجود ندارد',\r
+            DB_ERROR_NOSUCHTABLE        => 'چنین جدولی وجود ندارد',\r
+            DB_ERROR_NOT_CAPABLE        => 'backend بانک اطلاعاتی قادر نیست',\r
+            DB_ERROR_NOT_FOUND          => 'پیدا نشد',\r
+            DB_ERROR_NOT_LOCKED         => 'قفل نشده',\r
+            DB_ERROR_SYNTAX             => 'خطای دستوری',\r
+            DB_ERROR_UNSUPPORTED        => 'پشتیبانی نمی شود',\r
+            DB_ERROR_VALUE_COUNT_ON_ROW => 'شمارش مقادیر روی ردیف',\r
+            DB_ERROR_INVALID_DSN        => 'DSN نامعتبر',\r
+            DB_ERROR_CONNECT_FAILED     => 'ارتباط برقرار نشد',\r
+            0                         => 'بدون خطا', // DB_OK\r
+            DB_ERROR_NEED_MORE_DATA     => 'داده ناکافی است',\r
+            DB_ERROR_EXTENSION_NOT_FOUND=> 'extension پیدا نشد',\r
+            DB_ERROR_NOSUCHDB           => 'چنین بانک اطلاعاتی وجود ندارد',\r
+            DB_ERROR_ACCESS_VIOLATION   => 'حق دسترسی ناکافی'\r
+);\r
+?>
\ No newline at end of file
index 7b0479b556a957579a91c0590ed31b1254bf34e8..5cf0fdccf4991cd7a0ebb53480ea8e9ddcc78621 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
index 504533cb4fdfac716bc6954185461a9266caac22..d0455d253300f724401b7305c397c24290321bd4 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
index 6dd12c74807a0aaa3f6cb17955054d681165681e..6fc22426d957cf5feac644755602c4da52f7ddcc 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
diff --git a/lib/adodb/perf/perf-mssqlnative.inc.php b/lib/adodb/perf/perf-mssqlnative.inc.php
new file mode 100644 (file)
index 0000000..13b199c
--- /dev/null
@@ -0,0 +1,164 @@
+<?php
+
+/* 
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
+  
+  Latest version is available at http://adodb.sourceforge.net
+  
+  Library for basic performance monitoring and tuning 
+  
+*/
+
+// security - hide paths
+if (!defined('ADODB_DIR')) die();
+
+/*
+       MSSQL has moved most performance info to Performance Monitor
+*/
+class perf_mssqlnative extends adodb_perf{
+       var $sql1 = 'cast(sql1 as text)';
+       var $createTableSQL = "CREATE TABLE adodb_logsql (
+                 created datetime NOT NULL,
+                 sql0 varchar(250) NOT NULL,
+                 sql1 varchar(4000) NOT NULL,
+                 params varchar(3000) NOT NULL,
+                 tracer varchar(500) NOT NULL,
+                 timer decimal(16,6) NOT NULL
+               )";
+               
+       var $settings = array(
+       'Ratios',
+               'data cache hit ratio' => array('RATIO',
+                       "select round((a.cntr_value*100.0)/b.cntr_value,2) from master.dbo.sysperfinfo a, master.dbo.sysperfinfo b where a.counter_name = 'Buffer cache hit ratio' and b.counter_name='Buffer cache hit ratio base'",
+                       '=WarnCacheRatio'),
+               'prepared sql hit ratio' => array('RATIO',
+                       array('dbcc cachestats','Prepared',1,100),
+                       ''),
+               'adhoc sql hit ratio' => array('RATIO',
+                       array('dbcc cachestats','Adhoc',1,100),
+                       ''),
+       'IO',
+               'data reads' => array('IO',
+               "select cntr_value from master.dbo.sysperfinfo where counter_name = 'Page reads/sec'"),
+               'data writes' => array('IO',
+               "select cntr_value from master.dbo.sysperfinfo where counter_name = 'Page writes/sec'"),
+                       
+       'Data Cache',
+               'data cache size' => array('DATAC',
+               "select cntr_value*8192 from master.dbo.sysperfinfo where counter_name = 'Total Pages' and object_name='SQLServer:Buffer Manager'",
+                       '' ),
+               'data cache blocksize' => array('DATAC',
+                       "select 8192",'page size'),
+       'Connections',
+               'current connections' => array('SESS',
+                       '=sp_who',
+                       ''),
+               'max connections' => array('SESS',
+                       "SELECT @@MAX_CONNECTIONS",
+                       ''),
+
+               false
+       );
+       
+       
+       function perf_mssqlnative(&$conn)
+       {
+               if ($conn->dataProvider == 'odbc') {
+                       $this->sql1 = 'sql1';
+                       //$this->explain = false;
+               }
+               $this->conn =& $conn;
+       }
+       
+       function Explain($sql,$partial=false)
+       {
+               
+               $save = $this->conn->LogSQL(false);
+               if ($partial) {
+                       $sqlq = $this->conn->qstr($sql.'%');
+                       $arr = $this->conn->GetArray("select distinct sql1 from adodb_logsql where sql1 like $sqlq");
+                       if ($arr) {
+                               foreach($arr as $row) {
+                                       $sql = reset($row);
+                                       if (crc32($sql) == $partial) break;
+                               }
+                       }
+               }
+               
+               $s = '<p><b>Explain</b>: '.htmlspecialchars($sql).'</p>';
+               $this->conn->Execute("SET SHOWPLAN_ALL ON;");
+               $sql = str_replace('?',"''",$sql);
+               global $ADODB_FETCH_MODE;
+               
+               $save = $ADODB_FETCH_MODE;
+               $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+               $rs =& $this->conn->Execute($sql);
+               //adodb_printr($rs);
+               $ADODB_FETCH_MODE = $save;
+               if ($rs) {
+                       $rs->MoveNext();
+                       $s .= '<table bgcolor=white border=0 cellpadding="1" callspacing=0><tr><td nowrap align=center> Rows<td nowrap align=center> IO<td nowrap align=center> CPU<td align=left> &nbsp; &nbsp; Plan</tr>';
+                       while (!$rs->EOF) {
+                               $s .= '<tr><td>'.round($rs->fields[8],1).'<td>'.round($rs->fields[9],3).'<td align=right>'.round($rs->fields[10],3).'<td nowrap><pre>'.htmlspecialchars($rs->fields[0])."</td></pre></tr>\n"; ## NOTE CORRUPT </td></pre> tag is intentional!!!!
+                               $rs->MoveNext();
+                       }
+                       $s .= '</table>';
+                       
+                       $rs->NextRecordSet();
+               }
+               
+               $this->conn->Execute("SET SHOWPLAN_ALL OFF;");
+               $this->conn->LogSQL($save);
+               $s .= $this->Tracer($sql);
+               return $s;
+       }
+       
+       function Tables()
+       {
+       global $ADODB_FETCH_MODE;
+       
+               $save = $ADODB_FETCH_MODE;
+               $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+               //$this->conn->debug=1;
+               $s = '<table border=1 bgcolor=white><tr><td><b>tablename</b></td><td><b>size_in_k</b></td><td><b>index size</b></td><td><b>reserved size</b></td></tr>';
+               $rs1 = $this->conn->Execute("select distinct name from sysobjects where xtype='U'");
+               if ($rs1) {
+                       while (!$rs1->EOF) {
+                               $tab = $rs1->fields[0];
+                               $tabq = $this->conn->qstr($tab);
+                               $rs2 = $this->conn->Execute("sp_spaceused $tabq");
+                               if ($rs2) {
+                                       $s .= '<tr><td>'.$tab.'</td><td align=right>'.$rs2->fields[3].'</td><td align=right>'.$rs2->fields[4].'</td><td align=right>'.$rs2->fields[2].'</td></tr>';
+                                       $rs2->Close();
+                               }
+                               $rs1->MoveNext();
+                       }
+                       $rs1->Close();
+               }
+               $ADODB_FETCH_MODE = $save;
+               return $s.'</table>';
+       }
+       
+       function sp_who()
+       {
+               $arr = $this->conn->GetArray('sp_who');
+               return sizeof($arr);
+       }
+       
+       function HealthCheck($cli=false)
+       {
+               
+               $this->conn->Execute('dbcc traceon(3604)');
+               $html =  adodb_perf::HealthCheck($cli);
+               $this->conn->Execute('dbcc traceoff(3604)');
+               return $html;
+       }
+       
+       
+}
+
+?>
\ No newline at end of file
index ad79da6570349d68c2d1649d59a0873d17f729a1..955ce14b4c48894995928733ccc70465007ea464 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
@@ -16,270 +16,271 @@ V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights re
 if (!defined('ADODB_DIR')) die();
 
 class perf_mysql extends adodb_perf{
-    
-    var $tablesSQL = 'show table status';
-    
-    var $createTableSQL = "CREATE TABLE adodb_logsql (
-          created datetime NOT NULL,
-          sql0 varchar(250) NOT NULL,
-          sql1 text NOT NULL,
-          params text NOT NULL,
-          tracer text NOT NULL,
-          timer decimal(16,6) NOT NULL
-        )";
-        
-    var $settings = array(
-    'Ratios',
-        'MyISAM cache hit ratio' => array('RATIO',
-            '=GetKeyHitRatio',
-            '=WarnCacheRatio'),
-        'InnoDB cache hit ratio' => array('RATIO',
-            '=GetInnoDBHitRatio',
-            '=WarnCacheRatio'),
-        'data cache hit ratio' => array('HIDE', # only if called
-            '=FindDBHitRatio',
-            '=WarnCacheRatio'),
-        'sql cache hit ratio' => array('RATIO',
-            '=GetQHitRatio',
-            ''),
-    'IO',
-        'data reads' => array('IO',
-            '=GetReads',
-            'Number of selects (Key_reads is not accurate)'),
-        'data writes' => array('IO',
-            '=GetWrites',
-            'Number of inserts/updates/deletes * coef (Key_writes is not accurate)'),
-        
-    'Data Cache',
-        'MyISAM data cache size' => array('DATAC',
-            array("show variables", 'key_buffer_size'),
-            '' ),
-        'BDB data cache size' => array('DATAC',
-            array("show variables", 'bdb_cache_size'),
-            '' ),
-        'InnoDB data cache size' => array('DATAC',
-            array("show variables", 'innodb_buffer_pool_size'),
-            '' ),
-    'Memory Usage',
-        'read buffer size' => array('CACHE',
-            array("show variables", 'read_buffer_size'),
-            '(per session)'),
-        'sort buffer size' => array('CACHE',
-            array("show variables", 'sort_buffer_size'),
-            'Size of sort buffer (per session)' ),
-        'table cache' => array('CACHE',
-            array("show variables", 'table_cache'),
-            'Number of tables to keep open'),
-    'Connections',  
-        'current connections' => array('SESS',
-            array('show status','Threads_connected'),
-            ''),
-        'max connections' => array( 'SESS',
-            array("show variables",'max_connections'),
-            ''),
-    
-        false
-    );
-    
-    function perf_mysql(&$conn)
-    {
-        $this->conn = $conn;
-    }
-    
-    function Explain($sql,$partial=false)
-    {
-        
-        if (strtoupper(substr(trim($sql),0,6)) !== 'SELECT') return '<p>Unable to EXPLAIN non-select statement</p>';
-        $save = $this->conn->LogSQL(false);
-        if ($partial) {
-            $sqlq = $this->conn->qstr($sql.'%');
-            $arr = $this->conn->GetArray("select distinct sql1 from adodb_logsql where sql1 like $sqlq");
-            if ($arr) {
-                foreach($arr as $row) {
-                    $sql = reset($row);
-                    if (crc32($sql) == $partial) break;
-                }
-            }
-        }
-        $sql = str_replace('?',"''",$sql);
-        
-        if ($partial) {
-            $sqlq = $this->conn->qstr($sql.'%');
-            $sql = $this->conn->GetOne("select sql1 from adodb_logsql where sql1 like $sqlq");
-        }
-        
-        $s = '<p><b>Explain</b>: '.htmlspecialchars($sql).'</p>';
-        $rs = $this->conn->Execute('EXPLAIN '.$sql);
-        $s .= rs2html($rs,false,false,false,false);
-        $this->conn->LogSQL($save);
-        $s .= $this->Tracer($sql);
-        return $s;
-    }
-    
-    function Tables()
-    {
-        if (!$this->tablesSQL) return false;
-        
-        $rs = $this->conn->Execute($this->tablesSQL);
-        if (!$rs) return false;
-        
-        $html = rs2html($rs,false,false,false,false);
-        return $html;
-    }
-    
-    function GetReads()
-    {
-    global $ADODB_FETCH_MODE;
-        $save = $ADODB_FETCH_MODE;
-        $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
-        if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
-        
-        $rs = $this->conn->Execute('show status');
-        
-        if (isset($savem)) $this->conn->SetFetchMode($savem);
-        $ADODB_FETCH_MODE = $save;
-        
-        if (!$rs) return 0;
-        $val = 0;
-        while (!$rs->EOF) {
-            switch($rs->fields[0]) {
-            case 'Com_select': 
-                $val = $rs->fields[1];
-                $rs->Close();
-                return $val;
-            }
-            $rs->MoveNext();
-        } 
-        
-        $rs->Close();
-        
-        return $val;
-    }
-    
-    function GetWrites()
-    {
-    global $ADODB_FETCH_MODE;
-        $save = $ADODB_FETCH_MODE;
-        $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
-        if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
-        
-        $rs = $this->conn->Execute('show status');
-        
-        if (isset($savem)) $this->conn->SetFetchMode($savem);
-        $ADODB_FETCH_MODE = $save;
-        
-        if (!$rs) return 0;
-        $val = 0.0;
-        while (!$rs->EOF) {
-            switch($rs->fields[0]) {
-            case 'Com_insert': 
-                $val += $rs->fields[1]; break;
-            case 'Com_delete': 
-                $val += $rs->fields[1]; break;
-            case 'Com_update': 
-                $val += $rs->fields[1]/2;
-                $rs->Close();
-                return $val;
-            }
-            $rs->MoveNext();
-        } 
-        
-        $rs->Close();
-        
-        return $val;
-    }
-    
-    function FindDBHitRatio()
-    {
-        // first find out type of table
-        //$this->conn->debug=1;
-        
-        global $ADODB_FETCH_MODE;
-        $save = $ADODB_FETCH_MODE;
-        $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
-        if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
-        
-        $rs = $this->conn->Execute('show table status');
-        
-        if (isset($savem)) $this->conn->SetFetchMode($savem);
-        $ADODB_FETCH_MODE = $save;
-        
-        if (!$rs) return '';
-        $type = strtoupper($rs->fields[1]);
-        $rs->Close();
-        switch($type){
-        case 'MYISAM':
-        case 'ISAM':
-            return $this->DBParameter('MyISAM cache hit ratio').' (MyISAM)';
-        case 'INNODB':
-            return $this->DBParameter('InnoDB cache hit ratio').' (InnoDB)';
-        default:
-            return $type.' not supported';
-        }
-        
-    }
-    
-    function GetQHitRatio()
-    {
-        //Total number of queries = Qcache_inserts + Qcache_hits + Qcache_not_cached
-        $hits = $this->_DBParameter(array("show status","Qcache_hits"));
-        $total = $this->_DBParameter(array("show status","Qcache_inserts"));
-        $total += $this->_DBParameter(array("show status","Qcache_not_cached"));
-        
-        $total += $hits;
-        if ($total) return round(($hits*100)/$total,2);
-        return 0;
-    }
-    
-    /*
-        Use session variable to store Hit percentage, because MySQL
-        does not remember last value of SHOW INNODB STATUS hit ratio
-        
-        # 1st query to SHOW INNODB STATUS
-        0.00 reads/s, 0.00 creates/s, 0.00 writes/s
-        Buffer pool hit rate 1000 / 1000
-        
-        # 2nd query to SHOW INNODB STATUS
-        0.00 reads/s, 0.00 creates/s, 0.00 writes/s
-        No buffer pool activity since the last printout
-    */
-    function GetInnoDBHitRatio()
-    {
-    global $ADODB_FETCH_MODE;
-    
-        $save = $ADODB_FETCH_MODE;
-        $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
-        if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
-        
-        $rs = $this->conn->Execute('show innodb status');
-        
-        if (isset($savem)) $this->conn->SetFetchMode($savem);
-        $ADODB_FETCH_MODE = $save;
-        
-        if (!$rs || $rs->EOF) return 0;
-        $stat = $rs->fields[0];
-        $rs->Close();
-        $at = strpos($stat,'Buffer pool hit rate');
-        $stat = substr($stat,$at,200);
-        if (preg_match('!Buffer pool hit rate\s*([0-9]*) / ([0-9]*)!',$stat,$arr)) {
-            $val = 100*$arr[1]/$arr[2];
-            $_SESSION['INNODB_HIT_PCT'] = $val;
-            return round($val,2);
-        } else {
-            if (isset($_SESSION['INNODB_HIT_PCT'])) return $_SESSION['INNODB_HIT_PCT'];
-            return 0;
-        }
-    }
-    
-    function GetKeyHitRatio()
-    {
-        $hits = $this->_DBParameter(array("show status","Key_read_requests"));
-        $reqs = $this->_DBParameter(array("show status","Key_reads"));
-        if ($reqs == 0) return 0;
-        
-        return round(($hits/($reqs+$hits))*100,2);
-    }
-    
+       
+       var $tablesSQL = 'show table status';
+       
+       var $createTableSQL = "CREATE TABLE adodb_logsql (
+                 created datetime NOT NULL,
+                 sql0 varchar(250) NOT NULL,
+                 sql1 text NOT NULL,
+                 params text NOT NULL,
+                 tracer text NOT NULL,
+                 timer decimal(16,6) NOT NULL
+               )";
+               
+       var $settings = array(
+       'Ratios',
+               'MyISAM cache hit ratio' => array('RATIO',
+                       '=GetKeyHitRatio',
+                       '=WarnCacheRatio'),
+               'InnoDB cache hit ratio' => array('RATIO',
+                       '=GetInnoDBHitRatio',
+                       '=WarnCacheRatio'),
+               'data cache hit ratio' => array('HIDE', # only if called
+                       '=FindDBHitRatio',
+                       '=WarnCacheRatio'),
+               'sql cache hit ratio' => array('RATIO',
+                       '=GetQHitRatio',
+                       ''),
+       'IO',
+               'data reads' => array('IO',
+                       '=GetReads',
+                       'Number of selects (Key_reads is not accurate)'),
+               'data writes' => array('IO',
+                       '=GetWrites',
+                       'Number of inserts/updates/deletes * coef (Key_writes is not accurate)'),
+               
+       'Data Cache',
+               'MyISAM data cache size' => array('DATAC',
+                       array("show variables", 'key_buffer_size'),
+                       '' ),
+               'BDB data cache size' => array('DATAC',
+                       array("show variables", 'bdb_cache_size'),
+                       '' ),
+               'InnoDB data cache size' => array('DATAC',
+                       array("show variables", 'innodb_buffer_pool_size'),
+                       '' ),
+       'Memory Usage',
+               'read buffer size' => array('CACHE',
+                       array("show variables", 'read_buffer_size'),
+                       '(per session)'),
+               'sort buffer size' => array('CACHE',
+                       array("show variables", 'sort_buffer_size'),
+                       'Size of sort buffer (per session)' ),
+               'table cache' => array('CACHE',
+                       array("show variables", 'table_cache'),
+                       'Number of tables to keep open'),
+       'Connections',  
+               'current connections' => array('SESS',
+                       array('show status','Threads_connected'),
+                       ''),
+               'max connections' => array( 'SESS',
+                       array("show variables",'max_connections'),
+                       ''),
+       
+               false
+       );
+       
+       function perf_mysql(&$conn)
+       {
+               $this->conn = $conn;
+       }
+       
+       function Explain($sql,$partial=false)
+       {
+               
+               if (strtoupper(substr(trim($sql),0,6)) !== 'SELECT') return '<p>Unable to EXPLAIN non-select statement</p>';
+               $save = $this->conn->LogSQL(false);
+               if ($partial) {
+                       $sqlq = $this->conn->qstr($sql.'%');
+                       $arr = $this->conn->GetArray("select distinct sql1 from adodb_logsql where sql1 like $sqlq");
+                       if ($arr) {
+                               foreach($arr as $row) {
+                                       $sql = reset($row);
+                                       if (crc32($sql) == $partial) break;
+                               }
+                       }
+               }
+               $sql = str_replace('?',"''",$sql);
+               
+               if ($partial) {
+                       $sqlq = $this->conn->qstr($sql.'%');
+                       $sql = $this->conn->GetOne("select sql1 from adodb_logsql where sql1 like $sqlq");
+               }
+               
+               $s = '<p><b>Explain</b>: '.htmlspecialchars($sql).'</p>';
+               $rs = $this->conn->Execute('EXPLAIN '.$sql);
+               $s .= rs2html($rs,false,false,false,false);
+               $this->conn->LogSQL($save);
+               $s .= $this->Tracer($sql);
+               return $s;
+       }
+       
+       function Tables()
+       {
+               if (!$this->tablesSQL) return false;
+               
+               $rs = $this->conn->Execute($this->tablesSQL);
+               if (!$rs) return false;
+               
+               $html = rs2html($rs,false,false,false,false);
+               return $html;
+       }
+       
+       function GetReads()
+       {
+       global $ADODB_FETCH_MODE;
+               $save = $ADODB_FETCH_MODE;
+               $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+               if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
+               
+               $rs = $this->conn->Execute('show status');
+               
+               if (isset($savem)) $this->conn->SetFetchMode($savem);
+               $ADODB_FETCH_MODE = $save;
+               
+               if (!$rs) return 0;
+               $val = 0;
+               while (!$rs->EOF) {
+                       switch($rs->fields[0]) {
+                       case 'Com_select': 
+                               $val = $rs->fields[1];
+                               $rs->Close();
+                               return $val;
+                       }
+                       $rs->MoveNext();
+               } 
+               
+               $rs->Close();
+               
+               return $val;
+       }
+       
+       function GetWrites()
+       {
+       global $ADODB_FETCH_MODE;
+               $save = $ADODB_FETCH_MODE;
+               $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+               if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
+               
+               $rs = $this->conn->Execute('show status');
+               
+               if (isset($savem)) $this->conn->SetFetchMode($savem);
+               $ADODB_FETCH_MODE = $save;
+               
+               if (!$rs) return 0;
+               $val = 0.0;
+               while (!$rs->EOF) {
+                       switch($rs->fields[0]) {
+                       case 'Com_insert': 
+                               $val += $rs->fields[1]; break;
+                       case 'Com_delete': 
+                               $val += $rs->fields[1]; break;
+                       case 'Com_update': 
+                               $val += $rs->fields[1]/2;
+                               $rs->Close();
+                               return $val;
+                       }
+                       $rs->MoveNext();
+               } 
+               
+               $rs->Close();
+               
+               return $val;
+       }
+       
+       function FindDBHitRatio()
+       {
+               // first find out type of table
+               //$this->conn->debug=1;
+               
+               global $ADODB_FETCH_MODE;
+               $save = $ADODB_FETCH_MODE;
+               $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+               if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
+               
+               $rs = $this->conn->Execute('show table status');
+               
+               if (isset($savem)) $this->conn->SetFetchMode($savem);
+               $ADODB_FETCH_MODE = $save;
+               
+               if (!$rs) return '';
+               $type = strtoupper($rs->fields[1]);
+               $rs->Close();
+               switch($type){
+               case 'MYISAM':
+               case 'ISAM':
+                       return $this->DBParameter('MyISAM cache hit ratio').' (MyISAM)';
+               case 'INNODB':
+                       return $this->DBParameter('InnoDB cache hit ratio').' (InnoDB)';
+               default:
+                       return $type.' not supported';
+               }
+               
+       }
+       
+       function GetQHitRatio()
+       {
+               //Total number of queries = Qcache_inserts + Qcache_hits + Qcache_not_cached
+               $hits = $this->_DBParameter(array("show status","Qcache_hits"));
+               $total = $this->_DBParameter(array("show status","Qcache_inserts"));
+               $total += $this->_DBParameter(array("show status","Qcache_not_cached"));
+               
+               $total += $hits;
+               if ($total) return round(($hits*100)/$total,2);
+               return 0;
+       }
+       
+       /*
+               Use session variable to store Hit percentage, because MySQL
+               does not remember last value of SHOW INNODB STATUS hit ratio
+               
+               # 1st query to SHOW INNODB STATUS
+               0.00 reads/s, 0.00 creates/s, 0.00 writes/s
+               Buffer pool hit rate 1000 / 1000
+               
+               # 2nd query to SHOW INNODB STATUS
+               0.00 reads/s, 0.00 creates/s, 0.00 writes/s
+               No buffer pool activity since the last printout
+       */
+       function GetInnoDBHitRatio()
+       {
+       global $ADODB_FETCH_MODE;
+       
+               $save = $ADODB_FETCH_MODE;
+               $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+               if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
+               
+               $rs = $this->conn->Execute('show innodb status');
+               
+               if (isset($savem)) $this->conn->SetFetchMode($savem);
+               $ADODB_FETCH_MODE = $save;
+               
+               if (!$rs || $rs->EOF) return 0;
+               $stat = $rs->fields[0];
+               $rs->Close();
+               $at = strpos($stat,'Buffer pool hit rate');
+               $stat = substr($stat,$at,200);
+               if (preg_match('!Buffer pool hit rate\s*([0-9]*) / ([0-9]*)!',$stat,$arr)) {
+                       $val = 100*$arr[1]/$arr[2];
+                       $_SESSION['INNODB_HIT_PCT'] = $val;
+                       return round($val,2);
+               } else {
+                       if (isset($_SESSION['INNODB_HIT_PCT'])) return $_SESSION['INNODB_HIT_PCT'];
+                       return 0;
+               }
+               return 0;
+       }
+       
+       function GetKeyHitRatio()
+       {
+               $hits = $this->_DBParameter(array("show status","Key_read_requests"));
+               $reqs = $this->_DBParameter(array("show status","Key_reads"));
+               if ($reqs == 0) return 0;
+               
+               return round(($hits/($reqs+$hits))*100,2);
+       }
+       
     // start hack 
     var $optimizeTableLow = 'CHECK TABLE %s FAST QUICK';
     var $optimizeTableHigh = 'OPTIMIZE TABLE %s';
@@ -311,4 +312,4 @@ class perf_mysql extends adodb_perf{
      }
     // end hack 
 }
-?>
+?>
\ No newline at end of file
index 751280516fcaa017676fdcb288786c140ccc5825..1ea47f9a44386ca08534cfbd5a095e7e0de2d630 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
@@ -16,11 +16,14 @@ V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights re
 if (!defined('ADODB_DIR')) die();
 
 class perf_oci8 extends ADODB_perf{
+
+       var $noShowIxora = 15; // if the sql for suspicious sql is taking too long, then disable ixora
        
        var $tablesSQL = "select segment_name as \"tablename\", sum(bytes)/1024 as \"size_in_k\",tablespace_name as \"tablespace\",count(*) \"extents\" from sys.user_extents 
           group by segment_name,tablespace_name";
         
        var $version;
+       
        var $createTableSQL = "CREATE TABLE adodb_logsql (
                  created date NOT NULL,
                  sql0 varchar(250) NOT NULL,
@@ -68,6 +71,7 @@ AND    b.name = 'sorts (memory)'",
                "select value from v\$sysstat where name='physical writes'"),
        
        'Data Cache',
+       
                'data cache buffers' => array( 'DATAC',
                "select a.value/b.value  from v\$parameter a, v\$parameter b 
                        where a.name = 'db_cache_size' and b.name= 'db_block_size'",
@@ -75,8 +79,15 @@ AND    b.name = 'sorts (memory)'",
                'data cache blocksize' => array('DATAC',
                        "select value from v\$parameter where name='db_block_size'",
                        '' ),                   
+       
        'Memory Pools',
-               'data cache size' => array('DATAC',
+               'SGA Max Size' => array( 'DATAC',
+               "select value from v\$parameter where name = 'sga_max_size'",
+                       'The sga_max_size is the maximum value to which sga_target can be set.' ),
+       'SGA target' => array( 'DATAC',
+               "select value from v\$parameter where name = 'sga_target'",
+                       'If sga_target is defined then data cache, shared, java and large pool size can be 0. This is because all these pools are consolidated into one sga_target.' ),
+       'data cache size' => array('DATAC',
                        "select value from v\$parameter where name = 'db_cache_size'",
                        'db_cache_size' ),
                'shared pool size' => array('DATAC',
@@ -430,7 +441,11 @@ order by
                if (isset($_GET['sql'])) return $this->_SuspiciousSQL($numsql);
                
                $s = '';
+               $timer = time();
                $s .= $this->_SuspiciousSQL($numsql);
+               $timer = time() - $timer;
+               
+               if ($timer > $this->noShowIxora) return $s;
                $s .= '<p>';
                
                $save = $ADODB_CACHE_MODE;
@@ -502,7 +517,11 @@ order by
                }
                
                $s = '';                
+               $timer = time();
                $s .= $this->_ExpensiveSQL($numsql);
+               $timer = time() - $timer;
+               if ($timer > $this->noShowIxora) return $s;
+               
                $s .= '<p>';
                $save = $ADODB_CACHE_MODE;
                $ADODB_CACHE_MODE = ADODB_FETCH_NUM;
@@ -536,11 +555,12 @@ BEGIN
        LOOP
          cnt := cnt + 1;
          DELETE FROM $perf_table WHERE ROWID=rec.rr;
-         IF cnt = 10000 THEN
+         IF cnt = 1000 THEN
                COMMIT;
                cnt := 0;
          END IF;
        END LOOP;
+       commit;
 END;";
 
                $ok = $this->conn->Execute($sql);
index 87436443fdc2d008a444f39e43828176eb92d1aa..136b83f979e5116c7504b28d010da4ba103fed7d 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /* 
-V5.04a 25 Mar 2008   (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009   (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
index 628ad3c1725f67a17834c5a75258fc948119fd3e..89789030d47f48a42aa96c726c0ee4f3512d747a 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /** 
- * @version V4.93 10 Oct 2006 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ * @version V4.93 10 Oct 2006 (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
index d530f88977e6e76c195dd9537365940384be32cb..2c84c58531bcc24bab8dddb35253a925351d89c6 100644 (file)
@@ -1,4 +1,4 @@
-Description of ADODB V5.04a library import into Moodle
+Description of ADODB V5.08 library import into Moodle
 
 Removed:
  * contrib/
@@ -13,20 +13,12 @@ Added:
  * index.html - prevent directory browsing on misconfigured servers
  * readme_moodle.txt - this file ;-)
 
-Our changes:
+Our changes: /// Look for "moodle" in adodb code
  * adodb-lib.inc.php - added support for "F" and "L" types in  _adodb_column_sql()
  * adodb-lib.inc.php - modify some debug output to be correct XHTML. MDL-12378.
        Reported to ADOdb at: http://phplens.com/lens/lensforum/msgs.php?id=17133
        Once fixed by adodb guys, we'll return to their official distro.
- * drivers/adodb-mysqli.inc.php - fixed problem with driver not detecting enums
-       in the MetaColumns() function. MDL-14215.
-       Reported to ADOdb at: http://phplens.com/lens/lensforum/msgs.php?id=17383
-       Once fixed by adodb guys, we'll return to their official distro.
- * drivers/adodb-mssql.ic.php - fixed problem with insert statements using placeholders
-       not able to return the Insert_ID() at all. MDL-14886.
-       Reported to ADOdb at: http://phplens.com/lens/lensforum/msgs.php?id=17068
-       Once fixed by adodb guys, we'll return to their official distro.
+
 skodak, iarenaza, moodler, stronk7
 
 $Id$
index 7033a806f5e56e5b8bae04c7f63148bf1dc8b6a1..6f3d646d7d66d97464892102c343d05f8116b991 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /** 
- * @version V4.93 10 Oct 2006 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ * @version V4.93 10 Oct 2006 (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
index eacd6685bf13a65f3c7b7a904d206777defdc050..57b8bd1eb8e549af4d7d298175de65462b95ceea 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /** 
- * @version V4.93 10 Oct 2006 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ * @version V4.93 10 Oct 2006 (c) 2000-2009 John Lim (jlim#natsoft.com). 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. 
@@ -73,9 +73,10 @@ function _adodb_export(&$rs,$sep,$sepreplace,$fp=false,$addtitles=true,$quote =
        if ($addtitles) {
                $fieldTypes = $rs->FieldTypesArray();
                reset($fieldTypes);
+               $i = 0;
                while(list(,$o) = each($fieldTypes)) {
-                       if (!$o) $v = '';
-                       else $v = $o->name;
+               
+                       $v = ($o) ? $o->name : 'Field'.($i++);
                        if ($escquote) $v = str_replace($quote,$escquotequote,$v);
                        $v = strip_tags(str_replace("\n", $replaceNewLine, str_replace("\r\n",$replaceNewLine,str_replace($sep,$sepreplace,$v))));
                        $elements[] = $v;
index c0135cbc48a89bd240999e7d084a9de483821d1a..6a4e3ba471ca4af061290c88321d7c9ea4697264 100644 (file)
@@ -1,13 +1,13 @@
 <?php 
 /*
-  V4.93 10 Oct 2006  (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+  V4.93 10 Oct 2006  (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
   
   Some pretty-printing by Chris Oxenreider <oxenreid@state.net>
 */ 
-  
+
 // specific code for tohtml
 GLOBAL $gSQLMaxRows,$gSQLBlockRows,$ADODB_ROUND;
 
@@ -84,14 +84,18 @@ GLOBAL $gSQLMaxRows,$gSQLBlockRows,$ADODB_ROUND;
                        $type = $typearr[$i];
                        switch($type) {
                        case 'D':
-                               if (empty($v)) $s .= "<TD> &nbsp; </TD>\n";
-                               else if (!strpos($v,':')) {
-                                       $s .= " <TD>".$rs->UserDate($v,"D d, M Y") ."&nbsp;</TD>\n";
+                               if (strpos($v,':') !== false);
+                               else {
+                                       if (empty($v)) {
+                                       $s .= "<TD> &nbsp; </TD>\n";
+                                       } else {
+                                               $s .= " <TD>".$rs->UserDate($v,"D d, M Y") ."</TD>\n";                          
+                                       }
+                                       break;
                                }
-                               break;
                        case 'T':
                                if (empty($v)) $s .= "<TD> &nbsp; </TD>\n";
-                               else $s .= "    <TD>".$rs->UserTimeStamp($v,"D d, M Y, h:i:s") ."&nbsp;</TD>\n";
+                               else $s .= "    <TD>".$rs->UserTimeStamp($v,"D d, M Y, H:i:s") ."</TD>\n";
                        break;
                        
                        case 'N':
@@ -100,7 +104,9 @@ GLOBAL $gSQLMaxRows,$gSQLBlockRows,$ADODB_ROUND;
                                else
                                        $v = round($v,$ADODB_ROUND);
                        case 'I':
-                               $s .= " <TD align=right>".stripslashes((trim($v))) ."&nbsp;</TD>\n";
+                               $vv = stripslashes((trim($v)));
+                               if (strlen($vv) == 0) $vv .= '&nbsp;';
+                               $s .= " <TD align=right>".$vv ."</TD>\n";
                                
                        break;
                        /*