diff --git a/lib/adodb/README.md b/lib/adodb/README.md index e432b2fb0af..96ac67b2900 100644 --- a/lib/adodb/README.md +++ b/lib/adodb/README.md @@ -1,8 +1,9 @@ ADOdb Library for PHP ====================== -[![Join chat on Gitter](https://img.shields.io/gitter/room/form-data/form-data.svg)](https://gitter.im/adodb/adodb?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Download ADOdb](https://img.shields.io/sourceforge/dm/adodb.svg)](https://sourceforge.net/projects/adodb/files/latest/download) +[![Join chat on Gitter](https://img.shields.io/gitter/room/adodb/adodb?logo=gitter)](https://gitter.im/adodb/adodb?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![SourceForge Downloads Shield](https://img.shields.io/sourceforge/dm/adodb?label=SourceForge&logo=sourceforge&color=informational)](https://sourceforge.net/projects/adodb/files/latest/download) +[![Packagist Downloads Shield](https://img.shields.io/packagist/dm/ADOdb/ADOdb-php?label=Packagist&logo=packagist&logoColor=white&color=informational)](https://packagist.org/packages/adodb/adodb-php) (c) 2000-2013 John Lim (jlim@natsoft.com) (c) 2014 Damien Regad, Mark Newnham and the diff --git a/lib/adodb/SECURITY.md b/lib/adodb/SECURITY.md new file mode 100644 index 00000000000..00670f24492 --- /dev/null +++ b/lib/adodb/SECURITY.md @@ -0,0 +1,45 @@ +# ADOdb Security Policy + +## Supported Versions + +The following releases of the library are currently being supported with +security updates. Please refer to the [project's home page](https://adodb.org) +for actual version numbers. + +- Stable +- Legacy +- Development (Git *master* branch) + +Older releases are no longer supported. + + +## Reporting a Vulnerability + +If you discover a vulnerability in ADOdb, please contact +the [project's maintainer](https://github.com/dregad) + +- by e-mail (look for it in the Git history) +- via private chat on [Gitter](https://gitter.im/dregad) + +Kindly provide the following information in your report: + +- Affected ADOdb version(s) or Git revision +- A clear and detailed description of the issue, including if possible a code + snippet to demonstrate or reproduce the vulnerability +- A patch for the issue if you have one, preferably in *Git diff* format + +### CVE handling + +To ensure a comprehensive and detailed declaration of the issue, we generally +prefer requesting CVE IDs ourselves, which usually happens after our analysis +confirms the vulnerability. + +In case you have already obtained a CVE ID, do not forget to reference it in +your report. + +### Credits + +Let us know if and how you wish to be credited for the finding. + +Your name, e-mail, company, etc. will be included as specified in the CVE +report, as well as in the Git commit message patching the issue. diff --git a/lib/adodb/adodb-active-record.inc.php b/lib/adodb/adodb-active-record.inc.php index 8f3b00538f6..ad40640a3ae 100644 --- a/lib/adodb/adodb-active-record.inc.php +++ b/lib/adodb/adodb-active-record.inc.php @@ -281,7 +281,7 @@ class ADODB_Active_Record { static function TableBelongsTo($table, $foreignRef, $foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record') { - $ar = new ADOdb_Active_Record($table); + $ar = new ADODB_Active_Record($table); $ar->belongsTo($foreignRef, $foreignKey, $parentKey, $parentClass); } @@ -290,7 +290,7 @@ class ADODB_Active_Record { if (!is_array($tablePKey)) { $tablePKey = array($tablePKey); } - $ar = new ADOdb_Active_Record($table, $tablePKey); + $ar = new ADODB_Active_Record($table, $tablePKey); $ar->belongsTo($foreignRef, $foreignKey, $parentKey, $parentClass); } diff --git a/lib/adodb/adodb-csvlib.inc.php b/lib/adodb/adodb-csvlib.inc.php index c81254bfc03..77e63bfb062 100644 --- a/lib/adodb/adodb-csvlib.inc.php +++ b/lib/adodb/adodb-csvlib.inc.php @@ -29,11 +29,13 @@ global $ADODB_INCLUDED_CSV; $ADODB_INCLUDED_CSV = 1; /** - * convert a recordset into special format + * Convert a recordset into special format * - * @param rs the recordset + * @param ADORecordSet $rs the recordset + * @param ADOConnection $conn + * @param string $sql * - * @return the CSV formatted data + * @return string the CSV formatted data */ function _rs2serialize(&$rs,$conn=false,$sql='') { @@ -74,7 +76,7 @@ $ADODB_INCLUDED_CSV = 1; $savefetch = isset($rs->adodbFetchMode) ? $rs->adodbFetchMode : $rs->fetchMode; $class = $rs->connection->arrayClass; - $rs2 = new $class(); + $rs2 = new $class(-1); // Dummy query Id $rs2->timeCreated = $rs->timeCreated; # memcache fix $rs2->sql = $rs->sql; $rs2->oldProvider = $rs->dataProvider; @@ -83,19 +85,19 @@ $ADODB_INCLUDED_CSV = 1; return $line.serialize($rs2); } - -/** -* Open CSV file and convert it into Data. -* -* @param url file/ftp/http url -* @param err returns the error message -* @param timeout dispose if recordset has been alive for $timeout secs -* -* @return recordset, or false if error occurred. If no -* error occurred in sql INSERT/UPDATE/DELETE, -* empty recordset is returned -*/ - function csv2rs($url,&$err,$timeout=0, $rsclass='ADORecordSet_array') + /** + * Open CSV file and convert it into Data. + * + * @param string $url file/ftp/http url + * @param string &$err returns the error message + * @param int $timeout dispose if recordset has been alive for $timeout secs + * @param string $rsclass RecordSet class to return + * + * @return ADORecordSet|false recordset, or false if error occurred. + * If no error occurred in sql INSERT/UPDATE/DELETE, + * empty recordset is returned. + */ + function csv2rs($url, &$err, $timeout=0, $rsclass='ADORecordSet_array') { $false = false; $err = false; diff --git a/lib/adodb/adodb-datadict.inc.php b/lib/adodb/adodb-datadict.inc.php index dbed95da714..9dfa07a6fcf 100644 --- a/lib/adodb/adodb-datadict.inc.php +++ b/lib/adodb/adodb-datadict.inc.php @@ -353,7 +353,7 @@ class ADODB_DataDict { function nameQuote($name = NULL,$allowBrackets=false) { if (!is_string($name)) { - return FALSE; + return false; } $name = trim($name); @@ -427,6 +427,15 @@ class ADODB_DataDict { function actualType($meta) { + $meta = strtoupper($meta); + + /* + * Add support for custom meta types. We do this + * first, that allows us to override existing types + */ + if (isset($this->connection->customMetaTypes[$meta])) + return $this->connection->customMetaTypes[$meta]['actual']; + return $meta; } @@ -498,7 +507,7 @@ class ADODB_DataDict { * @param string $tabname table-name * @param string $flds column-name and type for the changed column * @param string $tableflds='' complete definition of the new table, eg. for postgres, default '' - * @param array/string $tableoptions='' options for the new table see createTableSQL, default '' + * @param array|string $tableoptions='' options for the new table see createTableSQL, default '' * @return array with SQL strings */ function alterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') @@ -553,7 +562,7 @@ class ADODB_DataDict { * @param string $tabname table-name * @param string $flds column-name and type for the changed column * @param string $tableflds='' complete definition of the new table, eg. for postgres, default '' - * @param array/string $tableoptions='' options for the new table see createTableSQL, default '' + * @param array|string $tableoptions='' options for the new table see createTableSQL, default '' * @return array with SQL strings */ function dropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') @@ -695,16 +704,25 @@ class ADODB_DataDict { case '0': case 'NAME': $fname = $v; break; case '1': - case 'TYPE': $ty = $v; $ftype = $this->actualType(strtoupper($v)); break; + case 'TYPE': + + $ty = $v; + $ftype = $this->actualType(strtoupper($v)); + break; case 'SIZE': - $dotat = strpos($v,'.'); if ($dotat === false) $dotat = strpos($v,','); - if ($dotat === false) $fsize = $v; - else { - $fsize = substr($v,0,$dotat); - $fprec = substr($v,$dotat+1); - } - break; + $dotat = strpos($v,'.'); + if ($dotat === false) + $dotat = strpos($v,','); + if ($dotat === false) + $fsize = $v; + else { + + $fsize = substr($v,0,$dotat); + $fprec = substr($v,$dotat+1); + + } + break; case 'UNSIGNED': $funsigned = true; break; case 'AUTOINCREMENT': case 'AUTO': $fautoinc = true; $fnotnull = true; break; diff --git a/lib/adodb/adodb-exceptions.inc.php b/lib/adodb/adodb-exceptions.inc.php index 9f1176f0f69..560286a1847 100644 --- a/lib/adodb/adodb-exceptions.inc.php +++ b/lib/adodb/adodb-exceptions.inc.php @@ -45,6 +45,9 @@ var $database = ''; $s = "$dbms error: [$errno: $errmsg] in $fn($p1, '$user', '****', $p2)"; break; default: + //Prevent PHP warning if $p1 or $p2 are arrays. + $p1 = ( is_array($p1) ) ? 'Array' : $p1; + $p2 = ( is_array($p2) ) ? 'Array' : $p2; $s = "$dbms error: [$errno: $errmsg] in $fn($p1, $p2)"; break; } diff --git a/lib/adodb/adodb-loadbalancer.inc.php b/lib/adodb/adodb-loadbalancer.inc.php index 2e4f3928d3e..88f5d0fdc21 100644 --- a/lib/adodb/adodb-loadbalancer.inc.php +++ b/lib/adodb/adodb-loadbalancer.inc.php @@ -174,7 +174,7 @@ class ADOdbLoadBalancer * @param string $type Type of database connection, either: 'write' capable or 'readonly' * @return bool|int|string */ - private function getConnectionByWeight($type) + public function getConnectionByWeight($type) { if ($type == 'readonly') { $total_weight = $this->total_connection_weights['all']; @@ -233,7 +233,7 @@ class ADOdbLoadBalancer * @return bool|ADOConnection * @throws Exception */ - private function _getConnection($connection_id) + public function getConnectionById($connection_id) { if (isset($this->connections[$connection_id])) { $connection_obj = $this->connections[$connection_id]; @@ -261,6 +261,15 @@ class ADOdbLoadBalancer throw $e; // No connections left, reThrow exception so application can catch it. } + // Check to see if a connection test callback was defined, and if so execute it. + // This is useful for testing replication lag and such to ensure the connection is suitable to be used. + $test_connection_callback = $connection_obj->getConnectionTestCallback(); + if (is_callable($test_connection_callback) + && $test_connection_callback($connection_obj, $adodb_obj) !== TRUE + ) { + return false; + } + if (is_array($this->user_defined_session_init_sql)) { foreach ($this->user_defined_session_init_sql as $session_init_sql) { $adodb_obj->Execute($session_init_sql); @@ -298,9 +307,12 @@ class ADOdbLoadBalancer if ($connection_id !== false) { try { - $adodb_obj = $this->_getConnection($connection_id); - // $connection_obj = $this->connections[$connection_id]; - break; + $adodb_obj = $this->getConnectionById($connection_id); + if (is_object($adodb_obj)) { + break; //Found valid connection, continue with it. + } else { + throw new Exception('ADODB Connection Object does not exist. Perhaps LoadBalancer Database Connection Test Failed?'); + } } catch (Exception $e) { // Connection error, see if there are other connections to try still. $this->removeConnection($connection_id); @@ -315,6 +327,10 @@ class ADOdbLoadBalancer } } + if (!isset($connection_id)) { + throw new Exception('No connection available to use at this time! Type: ' . $type); + } + $this->last_connection_id[$type] = $connection_id; if ($pin_connection === true) { @@ -432,7 +448,7 @@ class ADOdbLoadBalancer && $connection_obj->getADOdbObject()->_connectionID !== false ) ) { - $adodb_obj = $this->_getConnection($key); + $adodb_obj = $this->getConnectionById($key); if (is_object($adodb_obj)) { $result_arr[] = $adodb_obj->Execute($sql, $inputarr); } @@ -595,6 +611,7 @@ class ADOdbLoadBalancer case 'binddate': case 'bindtimestamp': case 'setfetchmode': + case 'setcustommetatype': $type = false; // No connection necessary. break; @@ -686,6 +703,11 @@ class ADOdbLoadBalancerConnection */ protected $adodb_obj = false; + /** + * @var callable Closure + */ + protected $connection_test_callback = NULL; + /** * @var string Type of connection, either 'write' capable or 'readonly' */ @@ -761,6 +783,24 @@ class ADOdbLoadBalancerConnection return true; } + /** + * Anonymous function that is called and must return TRUE for the connection to be usable.* + * The first argument is the type of connection to test. + * Useful to check things like replication lag. + * @param callable $callback + * @return void + */ + function setConnectionTestCallback($callback) { + $this->connection_test_callback = $callback; + } + + /** + * @return callable|null + */ + function getConnectionTestCallback() { + return $this->connection_test_callback; + } + /** * Returns the ADODB object for this connection. * diff --git a/lib/adodb/adodb-memcache.lib.inc.php b/lib/adodb/adodb-memcache.lib.inc.php index 7f110e7493c..a251c9c328b 100644 --- a/lib/adodb/adodb-memcache.lib.inc.php +++ b/lib/adodb/adodb-memcache.lib.inc.php @@ -17,6 +17,8 @@ * * @copyright 2000-2013 John Lim * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community + * + * @noinspection PhpUnused */ // security - hide paths @@ -26,183 +28,393 @@ global $ADODB_INCLUDED_MEMCACHE; $ADODB_INCLUDED_MEMCACHE = 1; global $ADODB_INCLUDED_CSV; -if (empty($ADODB_INCLUDED_CSV)) include_once(ADODB_DIR.'/adodb-csvlib.inc.php'); +if (empty($ADODB_INCLUDED_CSV)) { + include_once(ADODB_DIR . '/adodb-csvlib.inc.php'); +} - class ADODB_Cache_MemCache { - var $createdir = false; // create caching directory structure? +class ADODB_Cache_MemCache +{ + /** + * @var bool Prevents parent class calling non-existant function + */ + public $createdir = false; - // $library will be populated with the proper library on connect - // and is used later when there are differences in specific calls - // between memcache and memcached - var $library = false; + /** + * @var array of hosts + */ + private $hosts; - //----------------------------- - // memcache specific variables + /** + * @var int Connection Port, uses default + */ + private $port; - var $hosts; // array of hosts - var $port = 11211; - var $compress = false; // memcache compression with zlib + /** + * @var bool memcache compression with zlib + */ + private $compress; - var $_connected = false; - var $_memcache = false; + /** + * @var array of options for memcached only + */ + private $options; - function __construct(&$obj) - { - $this->hosts = $obj->memCacheHost; - $this->port = $obj->memCachePort; - $this->compress = $obj->memCacheCompress; + /** + * @var bool Internal flag indicating successful connection + */ + private $isConnected = false; + + /** + * @var Memcache|Memcached Handle for the Memcache library + * + * Populated with the proper library on connect, used later when + * there are differences in specific calls between memcache and memcached + */ + private $memcacheLibrary = false; + + /** + * @var array New server feature controller lists available servers + */ + private $serverControllers = array(); + + /** + * @var array New server feature template uses granular server controller + */ + private $serverControllerTemplate = array( + 'host' => '', + 'port' => 11211, + 'weight' => 0, + ); + + /** + * An integer index into the libraries + * @see $libraries + */ + const MCLIB = 1; + const MCLIBD = 2; + + /** + * @var array Xrefs the library flag to the actual class name + */ + private $libraries = array( + self::MCLIB => 'Memcache', + self::MCLIBD => 'Memcached' + ); + + /** + * @var int An indicator of which library we are using + */ + private $libraryFlag; + + /** + * Class Constructor. + * + * @param ADOConnection $db + */ + public function __construct($db) + { + $this->hosts = $db->memCacheHost; + $this->port = $this->serverControllerTemplate['port'] = $db->memCachePort; + $this->compress = $db->memCacheCompress; + $this->options = $db->memCacheOptions; + } + + /** + * Return true if the current library is Memcached. + * @return bool + */ + public function isLibMemcached(): bool + { + return $this->libraryFlag == self::MCLIBD; + } + + /** + * Lazy connection. + * + * The connection only occurs on CacheExecute call. + * + * @param string $err + * + * @return bool success of connecting to a server + */ + public function connect(&$err) + { + // do we have memcache or memcached? see the note at adodb.org on memcache + if (class_exists('Memcache')) { + $this->libraryFlag = self::MCLIB; + } elseif (class_exists('Memcached')) { + $this->libraryFlag = self::MCLIBD; + } else { + $err = 'Neither the Memcache nor Memcached PECL extensions were found!'; + return false; } - // implement as lazy connection. The connection only occurs on CacheExecute call - function connect(&$err) - { - // do we have memcache or memcached? - if (class_exists('Memcache')) { - $this->library='Memcache'; - $memcache = new MemCache; - } elseif (class_exists('Memcached')) { - $this->library='Memcached'; - $memcache = new MemCached; - } else { - $err = 'Neither the Memcache nor Memcached PECL extensions were found!'; + $usedLibrary = $this->libraries[$this->libraryFlag]; + + /** @var Memcache|Memcached $memCache */ + $memCache = new $usedLibrary; + if (!$memCache) { + $err = 'Memcache library failed to initialize'; + return false; + } + + // Convert simple compression flag for memcached + if ($this->isLibMemcached()) { + $this->options[Memcached::OPT_COMPRESSION] = $this->compress; + } + + // Are there any options available for memcached + if ($this->isLibMemcached() && count($this->options) > 0) { + $optionSuccess = $memCache->setOptions($this->options); + if (!$optionSuccess) { + $err = 'Invalid option parameters passed to Memcached'; return false; } + } - if (!is_array($this->hosts)) $this->hosts = array($this->hosts); + // Have we passed a controller array + if (!is_array($this->hosts)) { + $this->hosts = array($this->hosts); + } - $failcnt = 0; - foreach($this->hosts as $host) { - if (!@$memcache->addServer($host,$this->port)) { - $failcnt += 1; + if (!is_array($this->hosts[0])) { + // Old way, convert to controller + foreach ($this->hosts as $ipAddress) { + $connector = $this->serverControllerTemplate; + $connector['host'] = $ipAddress; + $connector['port'] = $this->port; + + $this->serverControllers[] = $connector; + } + } else { + // New way, must validate port, etc + foreach ($this->hosts as $controller) { + $connector = array_merge($this->serverControllerTemplate, $controller); + if ($this->isLibMemcached()) { + $connector['weight'] = (int)$connector['weight']; + } else { + // Cannot use weight in memcache, simply discard + $connector['weight'] = 0; } + + $this->serverControllers[] = $connector; } - if ($failcnt == sizeof($this->hosts)) { - $err = 'Can\'t connect to any memcache server'; - return false; - } - $this->_connected = true; - $this->_memcache = $memcache; + } + + // Checks for existing connections ( but only for memcached ) + if ($this->isLibMemcached() && !empty($memCache->getServerList())) { + // Use the existing configuration + $this->isConnected = true; + $this->memcacheLibrary = $memCache; return true; } - // 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); + $failcnt = 0; + foreach ($this->serverControllers as $controller) { + if ($this->isLibMemcached()) { + if (!@$memCache->addServer($controller['host'], $controller['port'], $controller['weight'])) { + $failcnt++; + } + } else { + if (!@$memCache->addServer($controller['host'], $controller['port'])) { + $failcnt++; + } } - if (!$this->_memcache) return false; + } + if ($failcnt == sizeof($this->serverControllers)) { + $err = 'Can\'t connect to any memcache server'; + return false; + } - $failed=false; - switch ($this->library) { - case 'Memcache': - if (!$this->_memcache->set($filename, $contents, $this->compress ? MEMCACHE_COMPRESSED : 0, $secs2cache)) { - $failed=true; + $this->memcacheLibrary = $memCache; + + // A valid memcache connection is available + $this->isConnected = true; + return true; + } + + /** + * Writes a cached query to the server + * + * @param string $filename The MD5 of the query to cache + * @param string $contents The query results + * @param bool $debug + * @param int $secs2cache + * + * @return bool true or false. true if successful save + */ + public function writeCache($filename, $contents, $debug, $secs2cache) + { + $err = ''; + if (!$this->isConnected && $debug) { + // Call to writeCache() before connect(), try to connect + if (!$this->connect($err)) { + ADOConnection::outp($err); + } + } else { + if (!$this->isConnected) { + $this->connect($err); + } + } + + if (!$this->memcacheLibrary) { + return false; + } + + $failed = false; + switch ($this->libraryFlag) { + case self::MCLIB: + if (!$this->memcacheLibrary->set($filename, $contents, $this->compress ? MEMCACHE_COMPRESSED : 0, + $secs2cache)) { + $failed = true; + } + break; + case self::MCLIBD: + if (!$this->memcacheLibrary->set($filename, $contents, $secs2cache)) { + $failed = true; + } + break; + default: + $failed = true; + break; + } + + if ($failed) { + if ($debug) { + ADOConnection::outp(" Failed to save data at the memcache server!
\n"); + } + return false; + } + + return true; + } + + /** + * Reads a cached query from the server. + * + * @param string $filename The MD5 of the query to read + * @param string $err The query results + * @param int $secs2cache + * @param object $rsClass **UNUSED** + * + * @return object|bool record or false. + * + * @noinspection PhpUnusedParameterInspection + */ + public function readCache($filename, &$err, $secs2cache, $rsClass) + { + if (!$this->isConnected) { + $this->connect($err); + } + if (!$this->memcacheLibrary) { + return false; + } + + $rs = $this->memcacheLibrary->get($filename); + if (!$rs) { + $err = 'Item with such key doesn\'t exist on the memcache 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 'Memcached': - if (!$this->_memcache->set($filename, $contents, $secs2cache)) { - $failed=true; + case 1: + if ((rand() & 3) == 0) { + $err = "Timeout 1"; + return false; } break; default: - $failed=true; - break; + $err = "Timeout 0"; + return false; } - - if($failed) { - if ($debug) ADOConnection::outp(" Failed to save data at the memcache server!
\n"); - return false; - } - - return true; - } - - // 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 exist on the memcache 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; - } - } - return $rs; - } - - 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!
\n"); - else ADOConnection::outp("flushall: succeeded!
\n"); - - return $del; - } - - 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 memcache server!
\n"); - else ADOConnection::outp("flushcache: $key entry flushed from memcache server!
\n"); - - return $del; - } - - // not used for memcache - function createdir($dir, $hash) - { - return true; } + return $rs; } + + /** + * Flushes all of the stored memcache data + * + * @param bool $debug + * + * @return bool The response from the memcache server + */ + public function flushAll($debug = false) + { + if (!$this->isConnected) { + $err = ''; + if (!$this->connect($err) && $debug) { + ADOConnection::outp($err); + } + } + if (!$this->memcacheLibrary) { + return false; + } + + $del = $this->memcacheLibrary->flush(); + + if ($debug) { + if (!$del) { + ADOConnection::outp("flushall: failed!
\n"); + } else { + ADOConnection::outp("flushall: succeeded!
\n"); + } + } + + return $del; + } + + /** + * Flushes the contents of a specified query + * + * @param string $filename The MD5 of the query to flush + * @param bool $debug + * + * @return bool The response from the memcache server + */ + public function flushCache($filename, $debug = false) + { + if (!$this->isConnected) { + $err = ''; + if (!$this->connect($err) && $debug) { + ADOConnection::outp($err); + } + } + if (!$this->memcacheLibrary) { + return false; + } + + $del = $this->memcacheLibrary->delete($filename); + + if ($debug) { + if (!$del) { + ADOConnection::outp("flushcache: $filename entry doesn't exist on memcache server!
\n"); + } else { + ADOConnection::outp("flushcache: $filename entry flushed from memcache server!
\n"); + } + } + + return $del; + } + +} diff --git a/lib/adodb/adodb-xmlschema.inc.php b/lib/adodb/adodb-xmlschema.inc.php index 58e3affd6c5..32b8afa1e61 100644 --- a/lib/adodb/adodb-xmlschema.inc.php +++ b/lib/adodb/adodb-xmlschema.inc.php @@ -26,133 +26,119 @@ * @author Dan Cech */ -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 -*/ + * Debug on or off + */ if( !defined( 'XMLS_DEBUG' ) ) { define( 'XMLS_DEBUG', FALSE ); } /** -* Default prefix key -*/ + * Default prefix key + */ if( !defined( 'XMLS_PREFIX' ) ) { define( 'XMLS_PREFIX', '%%P' ); } /** -* Maximum length allowed for object prefix -*/ + * Maximum length allowed for object prefix + */ if( !defined( 'XMLS_PREFIX_MAXLEN' ) ) { define( 'XMLS_PREFIX_MAXLEN', 10 ); } /** -* Execute SQL inline as it is generated -*/ + * Execute SQL inline as it is generated + */ if( !defined( 'XMLS_EXECUTE_INLINE' ) ) { define( 'XMLS_EXECUTE_INLINE', FALSE ); } /** -* Continue SQL Execution if an error occurs? -*/ + * Continue SQL Execution if an error occurs? + */ if( !defined( 'XMLS_CONTINUE_ON_ERROR' ) ) { define( 'XMLS_CONTINUE_ON_ERROR', FALSE ); } /** -* Current Schema Version -*/ + * 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. -*/ + * 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. -*/ + * 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 -*/ + * 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 -*/ + * 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 object Parent + */ var $parent; /** - * var string current element - */ + * var string current element + */ var $currentElement; /** - * NOP - */ - function __construct( &$parent, $attributes = NULL ) { + * NOP + */ + function __construct( $parent, $attributes = NULL ) { $this->parent = $parent; } /** - * XML Callback to process start elements - * - * @access private - */ + * XML Callback to process start elements + * + * @access private + */ function _tag_open( &$parser, $tag, $attributes ) { } /** - * XML Callback to process CDATA elements - * - * @access private - */ + * XML Callback to process CDATA elements + * + * @access private + */ function _tag_cdata( &$parser, $cdata ) { } /** - * XML Callback to process end elements - * - * @access private - */ + * XML Callback to process end elements + * + * @access private + */ function _tag_close( &$parser, $tag ) { } @@ -162,110 +148,110 @@ class dbObject { } /** - * Destroys the object - */ + * Destroys the object + */ function destroy() { } /** - * 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. - */ + * 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. - */ + * 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. - */ + * 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 -*/ + * 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 string Table name + */ var $name; /** - * @var array Field specifier: Meta-information about each field - */ + * @var array Field specifier: Meta-information about each field + */ var $fields = array(); /** - * @var array List of table indexes. - */ + * @var array List of table indexes. + */ var $indexes = array(); /** - * @var array Table options: Table-level options - */ + * @var array Table options: Table-level options + */ var $opts = array(); /** - * @var string Field index: Keeps track of which field is currently being processed - */ + * @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 boolean Mark table for destruction + * @access private + */ var $drop_table; /** - * @var boolean Mark field for destruction (not yet implemented) - * @access private - */ + * @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 __construct( &$parent, $attributes = NULL ) { + * Iniitializes a new table object. + * + * @param string $prefix DB Object prefix + * @param array $attributes Array of table attributes. + */ + function __construct( $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 - */ + * 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 ); @@ -321,10 +307,10 @@ class dbTable extends dbObject { } /** - * XML Callback to process CDATA elements - * - * @access private - */ + * XML Callback to process CDATA elements + * + * @access private + */ function _tag_cdata( &$parser, $cdata ) { switch( $this->currentElement ) { // Table constraint @@ -345,10 +331,10 @@ class dbTable extends dbObject { } /** - * XML Callback to process end elements - * - * @access private - */ + * XML Callback to process end elements + * + * @access private + */ function _tag_close( &$parser, $tag ) { $this->currentElement = ''; @@ -366,11 +352,11 @@ class dbTable extends dbObject { } /** - * Adds an index to a table object - * - * @param array $attributes Index attributes - * @return object dbIndex object - */ + * 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 ); @@ -378,11 +364,11 @@ class dbTable extends dbObject { } /** - * Adds data to a table object - * - * @param array $attributes Data attributes - * @return object dbData object - */ + * 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 ); @@ -391,34 +377,34 @@ class dbTable extends dbObject { } /** - * 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 - */ + * 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 ); @@ -443,16 +429,16 @@ class dbTable extends dbObject { } /** - * 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 - */ + * 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; @@ -463,14 +449,14 @@ class dbTable extends dbObject { } /** - * 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 - */ + * 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; @@ -480,11 +466,11 @@ class dbTable extends dbObject { /** - * Generates the SQL that will create the table in the database - * - * @param object $xmls adoSchema object - * @return array Array containing table creation SQL - */ + * 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(); @@ -592,8 +578,8 @@ class dbTable extends dbObject { } /** - * Marks a field or table for destruction - */ + * Marks a field or table for destruction + */ function drop() { if( isset( $this->current_field ) ) { // Drop the current field @@ -610,61 +596,61 @@ class dbTable extends dbObject { } /** -* 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 -*/ + * 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 string Index name + */ var $name; /** - * @var array Index options: Index-level options - */ + * @var array Index options: Index-level options + */ var $opts = array(); /** - * @var array Indexed fields: Table columns included in this index - */ + * @var array Indexed fields: Table columns included in this index + */ var $columns = array(); /** - * @var boolean Mark index for destruction - * @access private - */ + * @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 __construct( &$parent, $attributes = NULL ) { + * Initializes the new dbIndex object. + * + * @param object $parent Parent object + * @param array $attributes Attributes + * + * @internal + */ + function __construct( $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 - */ + * 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 ); @@ -686,12 +672,12 @@ class dbIndex extends dbObject { } /** - * XML Callback to process CDATA elements - * - * Processes XML cdata. - * - * @access private - */ + * XML Callback to process CDATA elements + * + * Processes XML cdata. + * + * @access private + */ function _tag_cdata( &$parser, $cdata ) { switch( $this->currentElement ) { // Index field name @@ -704,10 +690,10 @@ class dbIndex extends dbObject { } /** - * XML Callback to process end elements - * - * @access private - */ + * XML Callback to process end elements + * + * @access private + */ function _tag_close( &$parser, $tag ) { $this->currentElement = ''; @@ -719,11 +705,11 @@ class dbIndex extends dbObject { } /** - * Adds a field to the index - * - * @param string $name Field name - * @return string Field list - */ + * Adds a field to the index + * + * @param string $name Field name + * @return string Field list + */ function addField( $name ) { $this->columns[$this->FieldID( $name )] = $name; @@ -732,11 +718,11 @@ class dbIndex extends dbObject { } /** - * Adds options to the index - * - * @param string $opt Comma-separated list of index options. - * @return string Option list - */ + * Adds options to the index + * + * @param string $opt Comma-separated list of index options. + * @return string Option list + */ function addIndexOpt( $opt ) { $this->opts[] = $opt; @@ -745,11 +731,11 @@ class dbIndex extends dbObject { } /** - * Generates the SQL that will create the index in the database - * - * @param object $xmls adoSchema object - * @return array Array containing index creation SQL - */ + * 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; @@ -766,21 +752,21 @@ class dbIndex extends dbObject { } /** - * Marks an index for destruction - */ + * 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 -*/ + * 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(); @@ -788,25 +774,25 @@ class dbData extends dbObject { var $row; /** - * Initializes the new dbIndex object. - * - * @param object $parent Parent object - * @param array $attributes Attributes - * - * @internal - */ - function __construct( &$parent, $attributes = NULL ) { + * Initializes the new dbIndex object. + * + * @param object $parent Parent object + * @param array $attributes Attributes + * + * @internal + */ + function __construct( $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 - */ + * 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 ); @@ -823,12 +809,12 @@ class dbData extends dbObject { } /** - * XML Callback to process CDATA elements - * - * Processes XML cdata. - * - * @access private - */ + * XML Callback to process CDATA elements + * + * Processes XML cdata. + * + * @access private + */ function _tag_cdata( &$parser, $cdata ) { switch( $this->currentElement ) { // Index field name @@ -841,10 +827,10 @@ class dbData extends dbObject { } /** - * XML Callback to process end elements - * - * @access private - */ + * XML Callback to process end elements + * + * @access private + */ function _tag_close( &$parser, $tag ) { $this->currentElement = ''; @@ -856,11 +842,11 @@ class dbData extends dbObject { } /** - * Adds a field to the index - * - * @param string $name Field name - * @return string Field list - */ + * 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']; @@ -873,11 +859,11 @@ class dbData extends dbObject { } /** - * Adds options to the index - * - * @param string $opt Comma-separated list of index options. - * @return string Option list - */ + * 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(); @@ -891,11 +877,11 @@ class dbData extends dbObject { } /** - * Generates the SQL that will create the index in the database - * - * @param object $xmls adoSchema object - * @return array Array containing index creation SQL - */ + * 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); @@ -909,7 +895,8 @@ class dbData extends dbObject { 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 ) ); + $keys = array_keys($table_fields); + $field_id = reset($keys); } else { continue; } @@ -961,40 +948,40 @@ class dbData extends dbObject { } /** -* Creates the SQL to execute a list of provided SQL queries -* -* @package axmls -* @access private -*/ + * 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 array List of SQL queries + */ var $queries = array(); /** - * @var string String used to build of a query line by line - */ + * @var string String used to build of a query line by line + */ var $query; /** - * @var string Query prefix key - */ + * @var string Query prefix key + */ var $prefixKey = ''; /** - * @var boolean Auto prefix enable (TRUE) - */ + * @var boolean Auto prefix enable (TRUE) + */ var $prefixMethod = 'AUTO'; /** - * Initializes the query set. - * - * @param object $parent Parent object - * @param array $attributes Attributes - */ - function __construct( &$parent, $attributes = NULL ) { + * Initializes the query set. + * + * @param object $parent Parent object + * @param array $attributes Attributes + */ + function __construct( $parent, $attributes = NULL ) { $this->parent = $parent; // Overrides the manual prefix key @@ -1019,11 +1006,11 @@ class dbQuerySet extends dbObject { } /** - * XML Callback to process start elements. Elements currently - * processed are: QUERY. - * - * @access private - */ + * XML Callback to process start elements. Elements currently + * processed are: QUERY. + * + * @access private + */ function _tag_open( &$parser, $tag, $attributes ) { $this->currentElement = strtoupper( $tag ); @@ -1044,8 +1031,8 @@ class dbQuerySet extends dbObject { } /** - * XML Callback to process CDATA elements - */ + * XML Callback to process CDATA elements + */ function _tag_cdata( &$parser, $cdata ) { switch( $this->currentElement ) { // Line of queryset SQL data @@ -1058,10 +1045,10 @@ class dbQuerySet extends dbObject { } /** - * XML Callback to process end elements - * - * @access private - */ + * XML Callback to process end elements + * + * @access private + */ function _tag_close( &$parser, $tag ) { $this->currentElement = ''; @@ -1081,10 +1068,10 @@ class dbQuerySet extends dbObject { } /** - * Re-initializes the query. - * - * @return boolean TRUE - */ + * Re-initializes the query. + * + * @return boolean TRUE + */ function newQuery() { $this->query = ''; @@ -1092,10 +1079,10 @@ class dbQuerySet extends dbObject { } /** - * Discards the existing query. - * - * @return boolean TRUE - */ + * Discards the existing query. + * + * @return boolean TRUE + */ function discardQuery() { unset( $this->query ); @@ -1103,11 +1090,11 @@ class dbQuerySet extends dbObject { } /** - * 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. - */ + * 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; @@ -1119,10 +1106,10 @@ class dbQuerySet extends dbObject { } /** - * Adds a completed query to the query list - * - * @return string SQL of added query - */ + * Adds a completed query to the query list + * + * @return string SQL of added query + */ function addQuery() { if( !isset( $this->query ) ) { return FALSE; @@ -1136,11 +1123,11 @@ class dbQuerySet extends dbObject { } /** - * Creates and returns the current query set - * - * @param object $xmls adoSchema object - * @return array Query set - */ + * 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 ) { @@ -1176,13 +1163,13 @@ class dbQuerySet extends dbObject { } /** - * 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. - */ + * 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; @@ -1212,98 +1199,98 @@ class dbQuerySet extends dbObject { } /** -* 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: 1.12 $ -* -* @package axmls -*/ + * 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 1.12 + * + * @package axmls + */ class adoSchema { /** - * @var array Array containing SQL queries to generate all objects - * @access private - */ + * @var array Array containing SQL queries to generate all objects + * @access private + */ var $sqlArray; /** - * @var object ADOdb connection object - * @access private - */ + * @var object ADOdb connection object + * @access private + */ var $db; /** - * @var object ADOdb Data Dictionary - * @access private - */ + * @var object ADOdb Data Dictionary + * @access private + */ var $dict; /** - * @var string Current XML element - * @access private - */ + * @var string Current XML element + * @access private + */ var $currentElement = ''; /** - * @var string If set (to 'ALTER' or 'REPLACE'), upgrade an existing database - * @access private - */ + * @var string If set (to 'ALTER' or 'REPLACE'), upgrade an existing database + * @access private + */ var $upgrade = ''; /** - * @var string Optional object prefix - * @access private - */ + * @var string Optional object prefix + * @access private + */ var $objectPrefix = ''; /** - * @var long System debug - * @access private - */ + * @var long System debug + * @access private + */ var $debug; /** - * @var string Regular expression to find schema version - * @access private - */ + * @var string Regular expression to find schema version + * @access private + */ var $versionRegex = '//'; /** - * @var string Current schema version - * @access private - */ + * @var string Current schema version + * @access private + */ var $schemaVersion; /** - * @var int Success of last Schema execution - */ + * @var int Success of last Schema execution + */ var $success; /** - * @var bool Execute SQL inline as it is generated - */ + * @var bool Execute SQL inline as it is generated + */ var $executeInline; /** - * @var bool Continue SQL execution if errors occur - */ + * @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. - */ + * 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 __construct( $db ) { $this->db = $db; $this->debug = $this->db->debug; @@ -1316,21 +1303,21 @@ class adoSchema { } /** - * 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 - */ + * 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; @@ -1361,18 +1348,19 @@ class adoSchema { } /** - * 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() - */ + * 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() + * @see ExecuteSchema() + */ function ExecuteInline( $mode = NULL ) { if( is_bool( $mode ) ) { $this->executeInline = $mode; @@ -1382,18 +1370,19 @@ class adoSchema { } /** - * 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() - */ + * 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() + * @see ExecuteSchema() + */ function ContinueOnError( $mode = NULL ) { if( is_bool( $mode ) ) { $this->continueOnError = $mode; @@ -1403,33 +1392,34 @@ class adoSchema { } /** - * 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 - */ + * 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() - */ + * 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() + * @see ParseSchemaString() + */ function ParseSchemaFile( $filename, $returnSchema = FALSE ) { // Open the file if( !($fp = fopen( $filename, 'r' )) ) { @@ -1472,16 +1462,16 @@ class adoSchema { } /** - * 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. - */ + * 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; @@ -1515,31 +1505,31 @@ class adoSchema { } /** - * 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 - */ + * 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. - */ + * 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 @@ -1551,18 +1541,20 @@ class adoSchema { } /** - * 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. - */ + * 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() + * @see ParseSchemaString() + * @see 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(); @@ -1582,28 +1574,28 @@ class adoSchema { } /** - * 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 - */ + * 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. - */ + * 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 ) ) { @@ -1622,12 +1614,12 @@ class adoSchema { } /** - * Create an xml parser - * - * @return object PHP XML parser object - * - * @access private - */ + * Create an xml parser + * + * @return object PHP XML parser object + * + * @access private + */ function create_parser() { // Create the parser $xmlParser = xml_parser_create(); @@ -1641,10 +1633,10 @@ class adoSchema { } /** - * XML Callback to process start elements - * - * @access private - */ + * XML Callback to process start elements + * + * @access private + */ function _tag_open( &$parser, $tag, $attributes ) { switch( strtoupper( $tag ) ) { case 'TABLE': @@ -1664,39 +1656,39 @@ class adoSchema { } /** - * XML Callback to process CDATA elements - * - * @access private - */ + * XML Callback to process CDATA elements + * + * @access private + */ function _tag_cdata( &$parser, $cdata ) { } /** - * XML Callback to process end elements - * - * @access private - * @internal - */ + * 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. - */ + * 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 @@ -1722,29 +1714,22 @@ class adoSchema { 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. - */ + * 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 @@ -1757,7 +1742,7 @@ class adoSchema { } if( $version == $newVersion ) { - $result = _file_get_contents( $filename ); + $result = file_get_contents( $filename ); // remove unicode BOM if present if( substr( $result, 0, 3 ) == sprintf( '%c%c%c', 239, 187, 191 ) ) { @@ -1796,7 +1781,7 @@ class adoSchema { return FALSE; } - $schema = _file_get_contents( $schema ); + $schema = file_get_contents( $schema ); break; case 'string': default: @@ -1807,14 +1792,14 @@ class adoSchema { $arguments = array ( '/_xml' => $schema, - '/_xsl' => _file_get_contents( $xsl_file ) + '/_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')); + xslt_set_error_handler ($xh, array ($this, 'xslt_error_handler')); // process the schema $result = xslt_process ($xh, 'arg:/_xml', 'arg:/_xsl', NULL, $arguments); @@ -1825,15 +1810,15 @@ class adoSchema { } /** - * 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 - */ + * 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( @@ -1878,14 +1863,14 @@ class adoSchema { } /** - * 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 - */ + * 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' )) ) { @@ -1904,14 +1889,14 @@ class adoSchema { } /** - * 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 - */ + * 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; @@ -1925,15 +1910,15 @@ class adoSchema { } /** - * 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 - */ + * 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 ); @@ -2032,15 +2017,15 @@ class adoSchema { } /** - * 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 - */ + * 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 @@ -2067,13 +2052,13 @@ class adoSchema { } /** - * Returns an object name with the current prefix prepended. - * - * @param string $name Name - * @return string Prefixed name - * - * @access private - */ + * 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 ) ) { @@ -2087,13 +2072,13 @@ class adoSchema { } /** - * Checks if element references a specific platform - * - * @param string $platform Requested platform - * @returns boolean TRUE if platform check succeeds - * - * @access private - */ + * 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*)*$/'; @@ -2107,22 +2092,22 @@ class adoSchema { } /** - * Clears the array of generated SQL. - * - * @access private - */ + * 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 - */ + * 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 ) { @@ -2158,13 +2143,13 @@ class adoSchema { } /** - * Gets the SQL array in the specified format. - * - * @param string $format Format - * @return mixed SQL - * - * @access private - */ + * 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; @@ -2186,19 +2171,19 @@ class adoSchema { } /** - * 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. - */ + * 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() {} } /** -* Message logging function -* -* @access private -*/ + * Message logging function + * + * @access private + */ function logMsg( $msg, $title = NULL, $force = FALSE ) { if( XMLS_DEBUG or $force ) { echo '
';
diff --git a/lib/adodb/adodb-xmlschema03.inc.php b/lib/adodb/adodb-xmlschema03.inc.php
index de1ea26c7d6..a03386b3cc3 100644
--- a/lib/adodb/adodb-xmlschema03.inc.php
+++ b/lib/adodb/adodb-xmlschema03.inc.php
@@ -26,74 +26,60 @@
  * @author Dan Cech
  */
 
-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
-*/
+ * Debug on or off
+ */
 if( !defined( 'XMLS_DEBUG' ) ) {
 	define( 'XMLS_DEBUG', FALSE );
 }
 
 /**
-* Default prefix key
-*/
+ * Default prefix key
+ */
 if( !defined( 'XMLS_PREFIX' ) ) {
 	define( 'XMLS_PREFIX', '%%P' );
 }
 
 /**
-* Maximum length allowed for object prefix
-*/
+ * Maximum length allowed for object prefix
+ */
 if( !defined( 'XMLS_PREFIX_MAXLEN' ) ) {
 	define( 'XMLS_PREFIX_MAXLEN', 10 );
 }
 
 /**
-* Execute SQL inline as it is generated
-*/
+ * Execute SQL inline as it is generated
+ */
 if( !defined( 'XMLS_EXECUTE_INLINE' ) ) {
 	define( 'XMLS_EXECUTE_INLINE', FALSE );
 }
 
 /**
-* Continue SQL Execution if an error occurs?
-*/
+ * Continue SQL Execution if an error occurs?
+ */
 if( !defined( 'XMLS_CONTINUE_ON_ERROR' ) ) {
 	define( 'XMLS_CONTINUE_ON_ERROR', FALSE );
 }
 
 /**
-* Current Schema Version
-*/
+ * 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.
-*/
+ * 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).
-*/
+ * 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 );
 }
@@ -108,69 +94,69 @@ if( !defined( 'XMLS_EXISTING_DATA' ) ) {
 }
 
 /**
-* Default Schema Version.  Used for Schemas without an explicit version set.
-*/
+ * 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
-*/
+ * 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
-*/
+ * 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 object Parent
+	 */
 	var $parent;
 
 	/**
-	* var string current element
-	*/
+	 * var string current element
+	 */
 	var $currentElement;
 
 	/**
-	* NOP
-	*/
-	function __construct( &$parent, $attributes = NULL ) {
+	 * NOP
+	 */
+	function __construct( $parent, $attributes = NULL ) {
 		$this->parent = $parent;
 	}
 
 	/**
-	* XML Callback to process start elements
-	*
-	* @access private
-	*/
+	 * XML Callback to process start elements
+	 *
+	 * @access private
+	 */
 	function _tag_open( $parser, $tag, $attributes ) {
 
 	}
 
 	/**
-	* XML Callback to process CDATA elements
-	*
-	* @access private
-	*/
+	 * XML Callback to process CDATA elements
+	 *
+	 * @access private
+	 */
 	function _tag_cdata( $parser, $cdata ) {
 
 	}
 
 	/**
-	* XML Callback to process end elements
-	*
-	* @access private
-	*/
+	 * XML Callback to process end elements
+	 *
+	 * @access private
+	 */
 	function _tag_close( $parser, $tag ) {
 
 	}
@@ -180,117 +166,117 @@ class dbObject {
 	}
 
 	/**
-	* Destroys the object
-	*/
+	 * Destroys the object
+	 */
 	function destroy() {
 	}
 
 	/**
-	* 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.
-	*/
+	 * 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.
-	*/
+	 * 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.
-	*/
+	 * 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
-*/
+ * 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 string Table name
+	 */
 	var $name;
 
 	/**
-	* @var array Field specifier: Meta-information about each field
-	*/
+	 * @var array Field specifier: Meta-information about each field
+	 */
 	var $fields = array();
 
 	/**
-	* @var array List of table indexes.
-	*/
+	 * @var array List of table indexes.
+	 */
 	var $indexes = array();
 
 	/**
-	* @var array Table options: Table-level options
-	*/
+	 * @var array Table options: Table-level options
+	 */
 	var $opts = array();
 
 	/**
-	* @var string Field index: Keeps track of which field is currently being processed
-	*/
+	 * @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 boolean Mark table for destruction
+	 * @access private
+	 */
 	var $drop_table;
 
 	/**
-	* @var boolean Mark field for destruction (not yet implemented)
-	* @access private
-	*/
+	 * @var boolean Mark field for destruction (not yet implemented)
+	 * @access private
+	 */
 	var $drop_field = array();
 
 	/**
-	* @var array Platform-specific options
-	* @access private
-	*/
+	 * @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 __construct( &$parent, $attributes = NULL ) {
+	 * Iniitializes a new table object.
+	 *
+	 * @param string $prefix DB Object prefix
+	 * @param array $attributes Array of table attributes.
+	 */
+	function __construct( $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
-	*/
+	 * 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 );
 
@@ -349,10 +335,10 @@ class dbTable extends dbObject {
 	}
 
 	/**
-	* XML Callback to process CDATA elements
-	*
-	* @access private
-	*/
+	 * XML Callback to process CDATA elements
+	 *
+	 * @access private
+	 */
 	function _tag_cdata( $parser, $cdata ) {
 		switch( $this->currentElement ) {
 			// Table or field comment
@@ -385,10 +371,10 @@ class dbTable extends dbObject {
 	}
 
 	/**
-	* XML Callback to process end elements
-	*
-	* @access private
-	*/
+	 * XML Callback to process end elements
+	 *
+	 * @access private
+	 */
 	function _tag_close( $parser, $tag ) {
 		$this->currentElement = '';
 
@@ -411,11 +397,11 @@ class dbTable extends dbObject {
 	}
 
 	/**
-	* Adds an index to a table object
-	*
-	* @param array $attributes Index attributes
-	* @return object dbIndex object
-	*/
+	 * 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 );
@@ -423,11 +409,11 @@ class dbTable extends dbObject {
 	}
 
 	/**
-	* Adds data to a table object
-	*
-	* @param array $attributes Data attributes
-	* @return object dbData object
-	*/
+	 * 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 );
@@ -436,34 +422,34 @@ class dbTable extends dbObject {
 	}
 
 	/**
-	* 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
-	*/
+	 * 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 );
 
@@ -490,16 +476,16 @@ class dbTable extends dbObject {
 	}
 
 	/**
-	* 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
-	*/
+	 * 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 ) ) {
@@ -512,14 +498,14 @@ class dbTable extends dbObject {
 	}
 
 	/**
-	* 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
-	*/
+	 * 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->dataProvider] = $opt;
@@ -533,11 +519,11 @@ class dbTable extends dbObject {
 	}
 
 	/**
-	* Generates the SQL that will create the table in the database
-	*
-	* @param object $xmls adoSchema object
-	* @return array Array containing table creation SQL
-	*/
+	 * 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();
 
@@ -599,8 +585,11 @@ class dbTable extends dbObject {
 					// Option has an argument.
 					if( is_array( $opt ) ) {
 						$key = key( $opt );
-						$value = $opt[key( $opt )];
-						@$fldarray[$field_id][$key] .= $value;
+						$value = $opt[$key];
+						if(!isset($fldarray[$field_id][$key])) {
+							$fldarray[$field_id][$key] = "";
+						}
+						$fldarray[$field_id][$key] .= $value;
 					// Option doesn't have arguments
 					} else {
 						$fldarray[$field_id][$opt] = $opt;
@@ -645,8 +634,8 @@ class dbTable extends dbObject {
 	}
 
 	/**
-	* Marks a field or table for destruction
-	*/
+	 * Marks a field or table for destruction
+	 */
 	function drop() {
 		if( isset( $this->current_field ) ) {
 			// Drop the current field
@@ -663,61 +652,61 @@ class dbTable extends dbObject {
 }
 
 /**
-* 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
-*/
+ * 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 string	Index name
+	 */
 	var $name;
 
 	/**
-	* @var array	Index options: Index-level options
-	*/
+	 * @var array	Index options: Index-level options
+	 */
 	var $opts = array();
 
 	/**
-	* @var array	Indexed fields: Table columns included in this index
-	*/
+	 * @var array	Indexed fields: Table columns included in this index
+	 */
 	var $columns = array();
 
 	/**
-	* @var boolean Mark index for destruction
-	* @access private
-	*/
+	 * @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 __construct( &$parent, $attributes = NULL ) {
+	 * Initializes the new dbIndex object.
+	 *
+	 * @param object $parent Parent object
+	 * @param array $attributes Attributes
+	 *
+	 * @internal
+	 */
+	function __construct( $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
-	*/
+	 * 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 );
 
@@ -739,12 +728,12 @@ class dbIndex extends dbObject {
 	}
 
 	/**
-	* XML Callback to process CDATA elements
-	*
-	* Processes XML cdata.
-	*
-	* @access private
-	*/
+	 * XML Callback to process CDATA elements
+	 *
+	 * Processes XML cdata.
+	 *
+	 * @access private
+	 */
 	function _tag_cdata( $parser, $cdata ) {
 		switch( $this->currentElement ) {
 			// Index field name
@@ -757,10 +746,10 @@ class dbIndex extends dbObject {
 	}
 
 	/**
-	* XML Callback to process end elements
-	*
-	* @access private
-	*/
+	 * XML Callback to process end elements
+	 *
+	 * @access private
+	 */
 	function _tag_close( $parser, $tag ) {
 		$this->currentElement = '';
 
@@ -772,11 +761,11 @@ class dbIndex extends dbObject {
 	}
 
 	/**
-	* Adds a field to the index
-	*
-	* @param string $name Field name
-	* @return string Field list
-	*/
+	 * Adds a field to the index
+	 *
+	 * @param string $name Field name
+	 * @return string Field list
+	 */
 	function addField( $name ) {
 		$this->columns[$this->fieldID( $name )] = $name;
 
@@ -785,11 +774,11 @@ class dbIndex extends dbObject {
 	}
 
 	/**
-	* Adds options to the index
-	*
-	* @param string $opt Comma-separated list of index options.
-	* @return string Option list
-	*/
+	 * Adds options to the index
+	 *
+	 * @param string $opt Comma-separated list of index options.
+	 * @return string Option list
+	 */
 	function addIndexOpt( $opt ) {
 		$this->opts[] = $opt;
 
@@ -798,11 +787,11 @@ class dbIndex extends dbObject {
 	}
 
 	/**
-	* Generates the SQL that will create the index in the database
-	*
-	* @param object $xmls adoSchema object
-	* @return array Array containing index creation SQL
-	*/
+	 * 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;
@@ -819,22 +808,22 @@ class dbIndex extends dbObject {
 	}
 
 	/**
-	* Marks an index for destruction
-	*/
+	 * 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
-*/
+ * 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();
@@ -842,25 +831,25 @@ class dbData extends dbObject {
 	var $row;
 
 	/**
-	* Initializes the new dbData object.
-	*
-	* @param object $parent Parent object
-	* @param array $attributes Attributes
-	*
-	* @internal
-	*/
-	function __construct( &$parent, $attributes = NULL ) {
+	 * Initializes the new dbData object.
+	 *
+	 * @param object $parent Parent object
+	 * @param array $attributes Attributes
+	 *
+	 * @internal
+	 */
+	function __construct( $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
-	*/
+	 * 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 );
 
@@ -877,12 +866,12 @@ class dbData extends dbObject {
 	}
 
 	/**
-	* XML Callback to process CDATA elements
-	*
-	* Processes XML cdata.
-	*
-	* @access private
-	*/
+	 * XML Callback to process CDATA elements
+	 *
+	 * Processes XML cdata.
+	 *
+	 * @access private
+	 */
 	function _tag_cdata( $parser, $cdata ) {
 		switch( $this->currentElement ) {
 			// Index field name
@@ -895,10 +884,10 @@ class dbData extends dbObject {
 	}
 
 	/**
-	* XML Callback to process end elements
-	*
-	* @access private
-	*/
+	 * XML Callback to process end elements
+	 *
+	 * @access private
+	 */
 	function _tag_close( $parser, $tag ) {
 		$this->currentElement = '';
 
@@ -910,11 +899,11 @@ class dbData extends dbObject {
 	}
 
 	/**
-	* Adds a field to the insert
-	*
-	* @param string $name Field name
-	* @return string Field list
-	*/
+	 * 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] ) ) {
@@ -935,11 +924,11 @@ class dbData extends dbObject {
 	}
 
 	/**
-	* Adds options to the index
-	*
-	* @param string $opt Comma-separated list of index options.
-	* @return string Option list
-	*/
+	 * 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] ) ) {
@@ -949,11 +938,11 @@ class dbData extends dbObject {
 	}
 
 	/**
-	* Generates the SQL that will add/update the data in the database
-	*
-	* @param object $xmls adoSchema object
-	* @return array Array containing index creation SQL
-	*/
+	 * 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);
@@ -976,7 +965,8 @@ class dbData extends dbObject {
 			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 ) );
+						$keys = array_keys($table_fields);
+						$field_id = reset($keys);
 					} else {
 						continue;
 					}
@@ -1072,40 +1062,40 @@ class dbData extends dbObject {
 }
 
 /**
-* Creates the SQL to execute a list of provided SQL queries
-*
-* @package axmls
-* @access private
-*/
+ * 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 array	List of SQL queries
+	 */
 	var $queries = array();
 
 	/**
-	* @var string	String used to build of a query line by line
-	*/
+	 * @var string	String used to build of a query line by line
+	 */
 	var $query;
 
 	/**
-	* @var string	Query prefix key
-	*/
+	 * @var string	Query prefix key
+	 */
 	var $prefixKey = '';
 
 	/**
-	* @var boolean	Auto prefix enable (TRUE)
-	*/
+	 * @var boolean	Auto prefix enable (TRUE)
+	 */
 	var $prefixMethod = 'AUTO';
 
 	/**
-	* Initializes the query set.
-	*
-	* @param object $parent Parent object
-	* @param array $attributes Attributes
-	*/
-	function __construct( &$parent, $attributes = NULL ) {
+	 * Initializes the query set.
+	 *
+	 * @param object $parent Parent object
+	 * @param array $attributes Attributes
+	 */
+	function __construct( $parent, $attributes = NULL ) {
 		$this->parent = $parent;
 
 		// Overrides the manual prefix key
@@ -1130,11 +1120,11 @@ class dbQuerySet extends dbObject {
 	}
 
 	/**
-	* XML Callback to process start elements. Elements currently
-	* processed are: QUERY.
-	*
-	* @access private
-	*/
+	 * XML Callback to process start elements. Elements currently
+	 * processed are: QUERY.
+	 *
+	 * @access private
+	 */
 	function _tag_open( $parser, $tag, $attributes ) {
 		$this->currentElement = strtoupper( $tag );
 
@@ -1155,8 +1145,8 @@ class dbQuerySet extends dbObject {
 	}
 
 	/**
-	* XML Callback to process CDATA elements
-	*/
+	 * XML Callback to process CDATA elements
+	 */
 	function _tag_cdata( $parser, $cdata ) {
 		switch( $this->currentElement ) {
 			// Line of queryset SQL data
@@ -1169,10 +1159,10 @@ class dbQuerySet extends dbObject {
 	}
 
 	/**
-	* XML Callback to process end elements
-	*
-	* @access private
-	*/
+	 * XML Callback to process end elements
+	 *
+	 * @access private
+	 */
 	function _tag_close( $parser, $tag ) {
 		$this->currentElement = '';
 
@@ -1192,10 +1182,10 @@ class dbQuerySet extends dbObject {
 	}
 
 	/**
-	* Re-initializes the query.
-	*
-	* @return boolean TRUE
-	*/
+	 * Re-initializes the query.
+	 *
+	 * @return boolean TRUE
+	 */
 	function newQuery() {
 		$this->query = '';
 
@@ -1203,10 +1193,10 @@ class dbQuerySet extends dbObject {
 	}
 
 	/**
-	* Discards the existing query.
-	*
-	* @return boolean TRUE
-	*/
+	 * Discards the existing query.
+	 *
+	 * @return boolean TRUE
+	 */
 	function discardQuery() {
 		unset( $this->query );
 
@@ -1214,11 +1204,11 @@ class dbQuerySet extends dbObject {
 	}
 
 	/**
-	* 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.
-	*/
+	 * 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;
@@ -1230,10 +1220,10 @@ class dbQuerySet extends dbObject {
 	}
 
 	/**
-	* Adds a completed query to the query list
-	*
-	* @return string	SQL of added query
-	*/
+	 * Adds a completed query to the query list
+	 *
+	 * @return string	SQL of added query
+	 */
 	function addQuery() {
 		if( !isset( $this->query ) ) {
 			return FALSE;
@@ -1247,11 +1237,11 @@ class dbQuerySet extends dbObject {
 	}
 
 	/**
-	* Creates and returns the current query set
-	*
-	* @param object $xmls adoSchema object
-	* @return array Query set
-	*/
+	 * 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 ) {
@@ -1287,13 +1277,13 @@ class dbQuerySet extends dbObject {
 	}
 
 	/**
-	* 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.
-	*/
+	 * 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;
@@ -1323,103 +1313,103 @@ class dbQuerySet extends dbObject {
 }
 
 /**
-* 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: 1.62 $
-*
-* @package axmls
-*/
+ * 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 1.62
+ *
+ * @package axmls
+ */
 class adoSchema {
 
 	/**
-	* @var array	Array containing SQL queries to generate all objects
-	* @access private
-	*/
+	 * @var array	Array containing SQL queries to generate all objects
+	 * @access private
+	 */
 	var $sqlArray;
 
 	/**
-	* @var object	ADOdb connection object
-	* @access private
-	*/
+	 * @var object	ADOdb connection object
+	 * @access private
+	 */
 	var $db;
 
 	/**
-	* @var object	ADOdb Data Dictionary
-	* @access private
-	*/
+	 * @var object	ADOdb Data Dictionary
+	 * @access private
+	 */
 	var $dict;
 
 	/**
-	* @var string Current XML element
-	* @access private
-	*/
+	 * @var string Current XML element
+	 * @access private
+	 */
 	var $currentElement = '';
 
 	/**
-	* @var string If set (to 'ALTER' or 'REPLACE'), upgrade an existing database
-	* @access private
-	*/
+	 * @var string If set (to 'ALTER' or 'REPLACE'), upgrade an existing database
+	 * @access private
+	 */
 	var $upgrade = '';
 
 	/**
-	* @var string Optional object prefix
-	* @access private
-	*/
+	 * @var string Optional object prefix
+	 * @access private
+	 */
 	var $objectPrefix = '';
 
 	/**
-	* @var long	System debug
-	* @access private
-	*/
+	 * @var long	System debug
+	 * @access private
+	 */
 	var $debug;
 
 	/**
-	* @var string Regular expression to find schema version
-	* @access private
-	*/
+	 * @var string Regular expression to find schema version
+	 * @access private
+	 */
 	var $versionRegex = '//';
 
 	/**
-	* @var string Current schema version
-	* @access private
-	*/
+	 * @var string Current schema version
+	 * @access private
+	 */
 	var $schemaVersion;
 
 	/**
-	* @var int	Success of last Schema execution
-	*/
+	 * @var int	Success of last Schema execution
+	 */
 	var $success;
 
 	/**
-	* @var bool	Execute SQL inline as it is generated
-	*/
+	 * @var bool	Execute SQL inline as it is generated
+	 */
 	var $executeInline;
 
 	/**
-	* @var bool	Continue SQL execution if errors occur
-	*/
+	 * @var bool	Continue SQL execution if errors occur
+	 */
 	var $continueOnError;
 
 	/**
-	* @var int	How to handle existing data rows (insert, update, or ignore)
-	*/
+	 * @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.
-	*/
+	 * 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 __construct( $db ) {
 		$this->db = $db;
 		$this->debug = $this->db->debug;
@@ -1433,21 +1423,21 @@ class adoSchema {
 	}
 
 	/**
-	* 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
-	*/
+	 * 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;
@@ -1478,24 +1468,24 @@ class adoSchema {
 	}
 
 	/**
-	* 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
-	*/
+	 * 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 ) {
@@ -1519,18 +1509,19 @@ class adoSchema {
 	}
 
 	/**
-	* 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()
-	*/
+	 * 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()
+	 * @see ExecuteSchema()
+	 */
 	function executeInline( $mode = NULL ) {
 		if( is_bool( $mode ) ) {
 			$this->executeInline = $mode;
@@ -1540,18 +1531,19 @@ class adoSchema {
 	}
 
 	/**
-	* 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()
-	*/
+	 * 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()
+	 * @see ExecuteSchema()
+	 */
 	function continueOnError( $mode = NULL ) {
 		if( is_bool( $mode ) ) {
 			$this->continueOnError = $mode;
@@ -1561,43 +1553,44 @@ class adoSchema {
 	}
 
 	/**
-	* 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
-	*/
+	 * 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()
-	*/
+	 * 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()
+	 * @see parseSchemaString()
+	 */
 	function parseSchemaFile( $filename, $returnSchema = FALSE ) {
 		// Open the file
 		if( !($fp = fopen( $filename, 'r' )) ) {
@@ -1640,16 +1633,16 @@ class adoSchema {
 	}
 
 	/**
-	* 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.
-	*/
+	 * 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' );
@@ -1684,31 +1677,31 @@ class adoSchema {
 	}
 
 	/**
-	* 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
-	*/
+	 * 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.
-	*/
+	 * 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
@@ -1720,18 +1713,20 @@ class adoSchema {
 	}
 
 	/**
-	* 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.
-	*/
+	 * 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()
+	 * @see parseSchemaString()
+	 * @see 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();
@@ -1751,28 +1746,28 @@ class adoSchema {
 	}
 
 	/**
-	* 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
-	*/
+	 * 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.
-	*/
+	 * 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 ) ) {
@@ -1791,12 +1786,12 @@ class adoSchema {
 	}
 
 	/**
-	* Create an xml parser
-	*
-	* @return object PHP XML parser object
-	*
-	* @access private
-	*/
+	 * Create an xml parser
+	 *
+	 * @return object PHP XML parser object
+	 *
+	 * @access private
+	 */
 	function create_parser() {
 		// Create the parser
 		$xmlParser = xml_parser_create();
@@ -1810,10 +1805,10 @@ class adoSchema {
 	}
 
 	/**
-	* XML Callback to process start elements
-	*
-	* @access private
-	*/
+	 * XML Callback to process start elements
+	 *
+	 * @access private
+	 */
 	function _tag_open( $parser, $tag, $attributes ) {
 		switch( strtoupper( $tag ) ) {
 			case 'TABLE':
@@ -1835,39 +1830,39 @@ class adoSchema {
 	}
 
 	/**
-	* XML Callback to process CDATA elements
-	*
-	* @access private
-	*/
+	 * XML Callback to process CDATA elements
+	 *
+	 * @access private
+	 */
 	function _tag_cdata( $parser, $cdata ) {
 	}
 
 	/**
-	* XML Callback to process end elements
-	*
-	* @access private
-	* @internal
-	*/
+	 * 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.
-	*/
+	 * 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
@@ -1893,30 +1888,22 @@ class adoSchema {
 		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.
-	*/
+	 * 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
@@ -1929,7 +1916,7 @@ class adoSchema {
 		}
 
 		if( $version == $newVersion ) {
-			$result = _file_get_contents( $filename );
+			$result = file_get_contents( $filename );
 
 			// remove unicode BOM if present
 			if( substr( $result, 0, 3 ) == sprintf( '%c%c%c', 239, 187, 191 ) ) {
@@ -1968,7 +1955,7 @@ class adoSchema {
 					return FALSE;
 				}
 
-				$schema = _file_get_contents( $schema );
+				$schema = file_get_contents( $schema );
 				break;
 			case 'string':
 			default:
@@ -1979,14 +1966,14 @@ class adoSchema {
 
 		$arguments = array (
 			'/_xml' => $schema,
-			'/_xsl' => _file_get_contents( $xsl_file )
+			'/_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'));
+		xslt_set_error_handler ($xh, array ($this, 'xslt_error_handler'));
 
 		// process the schema
 		$result = xslt_process ($xh, 'arg:/_xml', 'arg:/_xsl', NULL, $arguments);
@@ -1997,15 +1984,15 @@ class adoSchema {
 	}
 
 	/**
-	* 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
-	*/
+	 * 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(
@@ -2050,14 +2037,14 @@ class adoSchema {
 	}
 
 	/**
-	* 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
-	*/
+	 * 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' )) ) {
@@ -2076,14 +2063,14 @@ class adoSchema {
 	}
 
 	/**
-	* 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
-	*/
+	 * 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;
@@ -2097,18 +2084,18 @@ class adoSchema {
 	}
 
 	/**
-	* 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
-	* tables in the schema.
-	*
-	* @param boolean $data include data in schema dump
-	* @param string $indent indentation to use
-	* @param string $prefix extract only tables with given prefix
-	* @param boolean $stripprefix strip prefix string when storing in XML schema
-	* @return string Generated XML schema
-	*/
+	 * 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
+	 * tables in the schema.
+	 *
+	 * @param boolean $data include data in schema dump
+	 * @param string $indent indentation to use
+	 * @param string $prefix extract only tables with given prefix
+	 * @param boolean $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 );
 
@@ -2217,15 +2204,15 @@ class adoSchema {
 	}
 
 	/**
-	* 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
-	*/
+	 * 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
@@ -2252,13 +2239,13 @@ class adoSchema {
 	}
 
 	/**
-	* Returns an object name with the current prefix prepended.
-	*
-	* @param string	$name Name
-	* @return string	Prefixed name
-	*
-	* @access private
-	*/
+	 * 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 ) ) {
@@ -2272,13 +2259,13 @@ class adoSchema {
 	}
 
 	/**
-	* Checks if element references a specific platform
-	*
-	* @param string $platform Requested platform
-	* @returns boolean TRUE if platform check succeeds
-	*
-	* @access private
-	*/
+	 * 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';
@@ -2301,22 +2288,22 @@ class adoSchema {
 	}
 
 	/**
-	* Clears the array of generated SQL.
-	*
-	* @access private
-	*/
+	 * 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
-	*/
+	 * 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 ) {
@@ -2352,13 +2339,13 @@ class adoSchema {
 	}
 
 	/**
-	* Gets the SQL array in the specified format.
-	*
-	* @param string $format Format
-	* @return mixed SQL
-	*
-	* @access private
-	*/
+	 * 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;
@@ -2380,20 +2367,20 @@ class adoSchema {
 	}
 
 	/**
-	* 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.
-	*/
+	 * 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() {
 	}
 }
 
 /**
-* Message logging function
-*
-* @access private
-*/
+ * Message logging function
+ *
+ * @access private
+ */
 function logMsg( $msg, $title = NULL, $force = FALSE ) {
 	if( XMLS_DEBUG or $force ) {
 		echo '
';
diff --git a/lib/adodb/adodb.inc.php b/lib/adodb/adodb.inc.php
index 3d5d1ad3c78..7ed8242b0db 100644
--- a/lib/adodb/adodb.inc.php
+++ b/lib/adodb/adodb.inc.php
@@ -70,19 +70,19 @@ if (!defined('_ADODB_LAYER')) {
 	//==============================================================================================
 
 	/*********************************************************
-	* Controls $ADODB_FORCE_TYPE mode. Default is ADODB_FORCE_VALUE (3).
-	* Used in GetUpdateSql and GetInsertSql functions. Thx to Niko, nuko#mbnet.fi
-	* @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:adodb_force_type
-	*
-	* 0 = ignore empty fields. All empty fields in array are ignored.
-	* 1 = force null. All empty, php null and string 'null' fields are
-	*     changed to sql NULL values.
-	* 2 = force empty. All empty, php null and string 'null' fields are
-	*     changed to sql empty '' or 0 values.
-	* 3 = force value. Value is left as it is. Php null and string 'null'
-	*     are set to sql NULL values and empty fields '' are set to empty '' sql values.
-	* 4 = force value. Like 1 but numeric empty fields are set to zero.
-    */
+	 * Controls $ADODB_FORCE_TYPE mode. Default is ADODB_FORCE_VALUE (3).
+	 * Used in GetUpdateSql and GetInsertSql functions. Thx to Niko, nuko#mbnet.fi
+	 * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:adodb_force_type
+	 *
+	 * 0 = ignore empty fields. All empty fields in array are ignored.
+	 * 1 = force null. All empty, php null and string 'null' fields are
+	 *     changed to sql NULL values.
+	 * 2 = force empty. All empty, php null and string 'null' fields are
+	 *     changed to sql empty '' or 0 values.
+	 * 3 = force value. Value is left as it is. Php null and string 'null'
+	 *     are set to sql NULL values and empty fields '' are set to empty '' sql values.
+	 * 4 = force value. Like 1 but numeric empty fields are set to zero.
+	 */
 		define('ADODB_FORCE_IGNORE',0);
 		define('ADODB_FORCE_NULL',1);
 		define('ADODB_FORCE_EMPTY',2);
@@ -99,10 +99,10 @@ if (!defined('_ADODB_LAYER')) {
 	define ('ADODB_STRINGMAX_NOLIMIT',-2);
 
 	/*
-	* Defines the the default meta type returned
-	* when ADOdb encounters a type that it is not
-	* defined in the metaTypes.
-	*/
+	 * Defines the the default meta type returned
+	 * when ADOdb encounters a type that it is not
+	 * defined in the metaTypes.
+	 */
 	if (!defined('ADODB_DEFAULT_METATYPE'))
 		define ('ADODB_DEFAULT_METATYPE','N');
 
@@ -198,7 +198,7 @@ if (!defined('_ADODB_LAYER')) {
 		/**
 		 * ADODB version as a string.
 		 */
-		$ADODB_vers = 'v5.21.4  2022-01-22';
+		$ADODB_vers = 'v5.22.2  2022-05-08';
 
 		/**
 		 * Determines whether recordset->RecordCount() is used.
@@ -240,12 +240,25 @@ if (!defined('_ADODB_LAYER')) {
 	}
 
 
+	/**
+	 * Parse date string to prevent injection attack.
+	 *
+	 * @param string $s
+	 *
+	 * @return string
+	 */
 	function _adodb_safedate($s) {
 		return str_replace(array("'", '\\'), '', $s);
 	}
 
-	// parse date string to prevent injection attack
-	// date string will have one quote at beginning e.g. '3434343'
+	/**
+	 * Parse date string to prevent injection attack.
+	 * Date string will have one quote at beginning e.g. '3434343'
+	 *
+	 * @param string $s
+	 *
+	 * @return string
+	 */
 	function _adodb_safedateq($s) {
 		$len = strlen($s);
 		if ($s[0] !== "'") {
@@ -269,9 +282,17 @@ if (!defined('_ADODB_LAYER')) {
 		return strlen($s2) == 0 ? 'null' : $s2;
 	}
 
-
-	// for transaction handling
-
+	/**
+	 * For transaction handling.
+	 *
+	 * @param $dbms
+	 * @param $fn
+	 * @param $errno
+	 * @param $errmsg
+	 * @param $p1
+	 * @param $p2
+	 * @param $thisConnection
+	 */
 	function ADODB_TransMonitor($dbms, $fn, $errno, $errmsg, $p1, $p2, &$thisConnection) {
 		//print "Errorno ($fn errno=$errno m=$errmsg) ";
 		$thisConnection->_transOK = false;
@@ -281,8 +302,9 @@ if (!defined('_ADODB_LAYER')) {
 		}
 	}
 
-	//------------------
-	// class for caching
+	/**
+	 * Class ADODB_Cache_File
+	 */
 	class ADODB_Cache_File {
 
 		var $createdir = true; // requires creation of temp dirs
@@ -294,18 +316,42 @@ if (!defined('_ADODB_LAYER')) {
 			}
 		}
 
-		// write serialised recordset to cache item/file
-		function writecache($filename, $contents,  $debug, $secs2cache) {
+		/**
+		 * Write serialised RecordSet to cache item/file.
+		 *
+		 * @param $filename
+		 * @param $contents
+		 * @param $debug
+		 * @param $secs2cache
+		 *
+		 * @return bool|int
+		 */
+		function writecache($filename, $contents, $debug, $secs2cache) {
 			return adodb_write_file($filename, $contents,$debug);
 		}
 
-		// load serialised recordset and unserialise it
+		/**
+		 * load serialised RecordSet and unserialise it
+		 *
+		 * @param $filename
+		 * @param $err
+		 * @param $secs2cache
+		 * @param $rsClass
+		 *
+		 * @return ADORecordSet
+		 */
 		function &readcache($filename, &$err, $secs2cache, $rsClass) {
 			$rs = csv2rs($filename,$err,$secs2cache,$rsClass);
 			return $rs;
 		}
 
-		// flush all items in cache
+		/**
+		 * Flush all items in cache.
+		 *
+		 * @param bool $debug
+		 *
+		 * @return bool|void
+		 */
 		function flushall($debug=false) {
 			global $ADODB_CACHE_DIR;
 
@@ -320,7 +366,12 @@ if (!defined('_ADODB_LAYER')) {
 			return $rez;
 		}
 
-		// flush one file in cache
+		/**
+		 * Flush one file in cache.
+		 *
+		 * @param string $f
+		 * @param bool   $debug
+		 */
 		function flushcache($f, $debug=false) {
 			if (!@unlink($f)) {
 				if ($debug) {
@@ -329,6 +380,11 @@ if (!defined('_ADODB_LAYER')) {
 			}
 		}
 
+		/**
+		 * @param string $hash
+		 *
+		 * @return string
+		 */
 		function getdirname($hash) {
 			global $ADODB_CACHE_DIR;
 			if (!isset($this->notSafeMode)) {
@@ -337,7 +393,14 @@ if (!defined('_ADODB_LAYER')) {
 			return ($this->notSafeMode) ? $ADODB_CACHE_DIR.'/'.substr($hash,0,2) : $ADODB_CACHE_DIR;
 		}
 
-		// create temp directories
+		/**
+		 * Create temp directories.
+		 *
+		 * @param string $hash
+		 * @param bool   $debug
+		 *
+		 * @return string
+		 */
 		function createdir($hash, $debug) {
 			global $ADODB_CACHE_PERMS;
 
@@ -396,6 +459,12 @@ if (!defined('_ADODB_LAYER')) {
 	var $dataProvider = 'native';
 	var $databaseType = '';		/// RDBMS currently in use, eg. odbc, mysql, mssql
 	var $database = '';			/// Name of database to be used.
+
+	/**
+	 * @var string If the driver is PDO, then the dsnType is e.g. sqlsrv, otherwise empty
+	 */
+	public $dsnType = '';
+
 	var $host = '';				/// The hostname of the database server
 	var $port = '';				/// The port of the database server
 	var $user = '';				/// The username which is used to connect to the database server.
@@ -440,11 +509,47 @@ if (!defined('_ADODB_LAYER')) {
 	var $isoDates = false;			/// accepts dates in ISO format
 	var $cacheSecs = 3600;			/// cache for 1 hour
 
-	// memcache
-	var $memCache = false; /// should we use memCache instead of caching in files
-	var $memCacheHost; /// memCache host
-	var $memCachePort = 11211; /// memCache port
-	var $memCacheCompress = false; /// Use 'true' to store the item compressed (uses zlib, not supported w/memcached library)
+	/*****************************************
+	* memcached server options
+	******************************************/
+
+	/**
+	 * Use memCache library instead of caching in files.
+	 * @var bool $memCache
+	 */
+	public $memCache = false;
+
+	/**
+	 * The memcache server(s) to connect to. Can be defined as:
+	 * - a single host name/ip address
+	 * - a list of hosts/ip addresses
+	 * - an array of server connection data (weighted server groups).
+	 * @link https://adodb.org/dokuwiki/doku.php?id=v5:userguide:memcached
+	 * @var string|array $memCacheHost
+	 */
+	public $memCacheHost;
+
+	/**
+	 * Default port number.
+	 * The default port can be overridden if memcache server connection data
+	 * is provided as an array {@see $memCacheHost}.
+	 * @var int $memCachePort
+	 */
+	public $memCachePort = 11211;
+
+	/**
+	 * Enable compression of stored items.
+	 * @var bool $memCacheCompress
+	 */
+	public $memCacheCompress = false;
+
+	/**
+	 * An array of memcached options.
+	 * Only used with memcached; memcache ignores this setting.
+	 * @link https://www.php.net/manual/en/memcached.constants.php
+	 * @var array $memCacheOptions
+	 */
+	public $memCacheOptions = array();
 
 	var $sysDate = false; /// name of function that returns the current date
 	var $sysTimeStamp = false; /// name of function that returns the current timestamp
@@ -512,6 +617,23 @@ if (!defined('_ADODB_LAYER')) {
 	 */
 	protected $connectionParameters = array();
 
+	/*
+	 * A simple associative array of user-defined custom actual/meta types
+	 */
+	public $customActualTypes = array();
+
+	/*
+	 * An array of user-defined custom meta/actual types.
+	 * $this->customMetaTypes[$meta] = array(
+	 *     'actual'=>'',
+	 *     'dictionary'=>'',
+	 *     'handler'=>'',
+	 *     'callback'=>''
+	 * );
+	 */
+	public $customMetaTypes = array();
+
+
 	/**
 	 * Default Constructor.
 	 * We define it even though it does not actually do anything. This avoids
@@ -572,11 +694,54 @@ if (!defined('_ADODB_LAYER')) {
 		return $matches[1];
 	}
 
+	/**
+	 * Set a custom meta type with a corresponding actual
+	 *
+	 * @param	string	$metaType	The Custom ADOdb metatype
+	 * @param	string	$dictionaryType	The database dictionary type
+	 * @param	string	$actualType	The database actual type
+	 * @param	bool	$handleAsType handle like an existing Metatype
+	 * @param	mixed	$callBack A pre-processing function
+	 *
+	 * @return bool success if the actual exists
+	 */
+	final public function setCustomMetaType(
+		$metaType,
+		$dictionaryType,
+		$actualType,
+		$handleAsType=false,
+		$callback=false){
+
+		$this->customMetaTypes[strtoupper($metaType)] = array(
+			'actual'=>$actualType,
+			'dictionary'=>strtoupper($dictionaryType),
+			'handler'=>$handleAsType,
+			'callback'=>$callback
+			);
+
+		/*
+		* Create a reverse lookup for the actualType
+		*/
+		$this->customActualTypes[$actualType] = $metaType;
+
+		return true;
+	}
+
+	/**
+	 * Get a list of custom meta types.
+	 *
+	 * @return string[]
+	 */
+	final public function getCustomMetaTypes()
+	{
+		return $this->customMetaTypes;
+	}
+
+
 	/**
 	 * Get server version info.
 	 *
-	 * @return string[] An array with 2 elements: $arr['string'] is the description string,
-	 *				 	and $arr[version] is the version (also a string).
+	 * @return string[] Array with 2 string elements: version and description
 	 */
 	function ServerInfo() {
 		return array('description' => '', 'version' => '');
@@ -591,6 +756,13 @@ if (!defined('_ADODB_LAYER')) {
 		return !empty($this->_connectionID);
 	}
 
+	/**
+	 * Find version string.
+	 *
+	 * @param string $str
+	 *
+	 * @return string
+	 */
 	function _findvers($str) {
 		if (preg_match('/([0-9]+\.([0-9\.])+)/',$str, $arr)) {
 			return $arr[1];
@@ -729,6 +901,16 @@ if (!defined('_ADODB_LAYER')) {
 		return false;
 	}
 
+	/**
+	 * Always force a new connection to database.
+	 *
+	 * @param string $argHostname     Host to connect to
+	 * @param string $argUsername     Userid to login
+	 * @param string $argPassword     Associated password
+	 * @param string $argDatabaseName Database name
+	 *
+	 * @return bool
+	 */
 	function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName) {
 		return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName);
 	}
@@ -807,7 +989,13 @@ if (!defined('_ADODB_LAYER')) {
 		return $ret;
 	}
 
-	function outp_throw($msg,$src='WARN',$sql='') {
+	/**
+	 * Throw an exception if the handler is defined or prints the message if not.
+	 * @param string $msg Message
+	 * @param string $src the name of the calling function (in uppercase)
+	 * @param string $sql Optional offending SQL statement
+	 */
+	function outp_throw($msg, $src='WARN', $sql='') {
 		if (defined('ADODB_ERROR_HANDLER') &&  ADODB_ERROR_HANDLER == 'adodb_throw') {
 			adodb_throw($this->databaseType,$src,-9999,$msg,$sql,false,$this);
 			return;
@@ -843,6 +1031,8 @@ if (!defined('_ADODB_LAYER')) {
 	 *
 	 * @param string $fmt Format string
 	 * @param string $col Date column; use system date if not specified.
+	 *
+	 * @return string
 	 */
 	function SQLDate($fmt, $col = '') {
 		if (!$col) {
@@ -852,14 +1042,14 @@ if (!defined('_ADODB_LAYER')) {
 	}
 
 	/**
-	 * Prepare an sql statement and return the statement resource.
+	 * Prepare an SQL statement and return the statement resource.
 	 *
-	 * For databases that do not support this, we return the $sql. To ensure
-	 * compatibility with databases that do not support prepare:
+	 * For databases that do not support prepared statements, we return the
+	 * provided SQL statement as-is, to ensure compatibility:
 	 *
-	 *   $stmt = $db->Prepare("insert into table (id, name) values (?,?)");
-	 *   $db->Execute($stmt,array(1,'Jill')) or die('insert failed');
-	 *   $db->Execute($stmt,array(2,'Joe')) or die('insert failed');
+	 *   $stmt = $db->prepare("insert into table (id, name) values (?,?)");
+	 *   $db->execute($stmt, array(1,'Jill')) or die('insert failed');
+	 *   $db->execute($stmt, array(2,'Joe')) or die('insert failed');
 	 *
 	 * @param string $sql SQL to send to database
 	 *
@@ -870,6 +1060,17 @@ if (!defined('_ADODB_LAYER')) {
 		return $sql;
 	}
 
+	/**
+	 * Releases a previously prepared statement.
+	 *
+	 * @param mixed $stmt Statement resource, as returned by {@see prepare()}
+	 *
+	 * @return bool
+	 */
+	function releaseStatement(&$stmt) {
+		return true;
+	}
+
 	/**
 	 * Prepare a Stored Procedure and return the statement resource.
 	 *
@@ -897,6 +1098,12 @@ if (!defined('_ADODB_LAYER')) {
 		return $this->qstr($s);
 	}
 
+	/**
+	 * Quotes a string so that all strings are escaped.
+	 * Wrapper for qstr with magic_quotes = false.
+	 *
+	 * @param string &$s
+	 */
 	function q(&$s) {
 		//if (!empty($this->qNull && $s == 'null') {
 		//	return $s;
@@ -905,8 +1112,9 @@ if (!defined('_ADODB_LAYER')) {
 	}
 
 	/**
-	* PEAR DB Compat - do not use internally.
-	*/
+	 * PEAR DB Compat - do not use internally.
+	 * @return int
+	 */
 	function ErrorNative() {
 		return $this->ErrorNo();
 	}
@@ -914,18 +1122,23 @@ if (!defined('_ADODB_LAYER')) {
 
 	/**
 	 * PEAR DB Compat - do not use internally.
+	 * @param string $seq_name
+	 * @return int
 	 */
 	function nextId($seq_name) {
 		return $this->GenID($seq_name);
 	}
 
 	/**
-	 * Lock a row, will escalate and lock the table if row locking not supported
-	 * will normally free the lock at the end of the transaction
+	 * Lock a row.
+	 * Will escalate and lock the table if row locking is not supported.
+	 * Will normally free the lock at the end of the transaction.
 	 *
-	 * @param string $table	name of table to lock
-	 * @param string $where	where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock
-     * @param string $col
+	 * @param string $table name of table to lock
+	 * @param string $where where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock
+	 * @param string $col
+	 *
+	 * @return bool
 	 */
 	function RowLock($table,$where,$col='1 as adodbignore') {
 		return false;
@@ -948,15 +1161,15 @@ if (!defined('_ADODB_LAYER')) {
 	}
 
 	/**
-	* PEAR DB Compat - do not use internally.
-	*
-	* The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical
-	* for easy porting :-)
-	*
-	* @param int $mode The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM
-	*
-	* @return int Previous fetch mode
-	*/
+	 * PEAR DB Compat - do not use internally.
+	 *
+	 * The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical
+	 * for easy porting :-)
+	 *
+	 * @param int $mode The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM
+	 *
+	 * @return int Previous fetch mode
+	 */
 	function SetFetchMode($mode) {
 		$old = $this->fetchMode;
 		$this->fetchMode = $mode;
@@ -968,15 +1181,14 @@ if (!defined('_ADODB_LAYER')) {
 		return $old;
 	}
 
-
 	/**
-	* PEAR DB Compat - do not use internally.
+	 * PEAR DB Compat - do not use internally.
 	 *
 	 * @param string     $sql
 	 * @param array|bool $inputarr
 	 *
 	 * @return ADORecordSet|bool
-	*/
+	 */
 	function Query($sql, $inputarr=false) {
 		$rs = $this->Execute($sql, $inputarr);
 		if (!$rs && defined('ADODB_PEAR')) {
@@ -985,7 +1197,6 @@ if (!defined('_ADODB_LAYER')) {
 		return $rs;
 	}
 
-
 	/**
 	 * PEAR DB Compat - do not use internally
 	 */
@@ -1026,36 +1237,54 @@ if (!defined('_ADODB_LAYER')) {
 		return '?';
 	}
 
-	/*
-		InParameter and OutParameter are self-documenting versions of Parameter().
-	*/
-	function InParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false) {
+	/**
+	 * Self-documenting version of Parameter().
+	 *
+	 * @param $stmt
+	 * @param &$var
+	 * @param $name
+	 * @param int $maxLen
+	 * @param bool $type
+	 *
+	 * @return bool
+	 */
+	function InParameter(&$stmt, &$var, $name, $maxLen=4000, $type=false) {
 		return $this->Parameter($stmt,$var,$name,false,$maxLen,$type);
 	}
 
-	/*
-	*/
+	/**
+	 * Self-documenting version of Parameter().
+	 *
+	 * @param $stmt
+	 * @param $var
+	 * @param $name
+	 * @param int $maxLen
+	 * @param bool $type
+	 *
+	 * @return bool
+	 */
 	function OutParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false) {
 		return $this->Parameter($stmt,$var,$name,true,$maxLen,$type);
 
 	}
 
-
-	/*
-	Usage in oracle
-		$stmt = $db->Prepare('select * from table where id =:myid and group=:group');
-		$db->Parameter($stmt,$id,'myid');
-		$db->Parameter($stmt,$group,'group',64);
-		$db->Execute();
-
-		@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.
-
-	*/
+	/**
+	 *
+	 * Usage in oracle
+	 *   $stmt = $db->Prepare('select * from table where id =:myid and group=:group');
+	 *   $db->Parameter($stmt,$id,'myid');
+	 *   $db->Parameter($stmt,$group,'group',64);
+	 *   $db->Execute();
+	 *
+	 * @param mixed &$stmt Statement returned by Prepare() or PrepareSP().
+	 * @param mixed &$var PHP variable to bind to
+	 * @param string $name Name of stored procedure variable name to bind to.
+	 * @param int|bool $isOutput Indicates direction of parameter 0/false=IN  1=OUT  2= IN/OUT. This is ignored in oci8.
+	 * @param int $maxLen Holds an maximum length of the variable.
+	 * @param mixed $type The data type of $var. Legal values depend on driver.
+	 *
+	 * @return bool
+	 */
 	function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false) {
 		return false;
 	}
@@ -1102,13 +1331,16 @@ if (!defined('_ADODB_LAYER')) {
 
 
 	/**
-		Used together with StartTrans() to end a transaction. Monitors connection
-		for sql errors, and will commit or rollback as appropriate.
-
-		@autoComplete if true, monitor sql errors and commit and rollback as appropriate,
-		and if set to false force rollback even if no SQL error detected.
-		@returns true on commit, false on rollback.
-	*/
+	 * Complete a transation.
+	 *
+	 * Used together with StartTrans() to end a transaction. Monitors connection
+	 * for sql errors, and will commit or rollback as appropriate.
+	 *
+	 * @param bool autoComplete if true, monitor sql errors and commit and
+	 *                          rollback as appropriate, and if set to false
+	 *                          force rollback even if no SQL error detected.
+	 * @returns true on commit, false on rollback.
+	 */
 	function CompleteTrans($autoComplete = true) {
 		if ($this->transOff > 1) {
 			$this->transOff -= 1;
@@ -1132,16 +1364,16 @@ if (!defined('_ADODB_LAYER')) {
 			$this->_transOK = false;
 			$this->RollbackTrans();
 			if ($this->debug) {
-				ADOCOnnection::outp("Smart Rollback occurred");
+				ADOConnection::outp("Smart Rollback occurred");
 			}
 		}
 
 		return $this->_transOK;
 	}
 
-	/*
-		At the end of a StartTrans/CompleteTrans block, perform a rollback.
-	*/
+	/**
+	 * At the end of a StartTrans/CompleteTrans block, perform a rollback.
+	 */
 	function FailTrans() {
 		if ($this->debug)
 			if ($this->transOff == 0) {
@@ -1154,8 +1386,8 @@ if (!defined('_ADODB_LAYER')) {
 	}
 
 	/**
-		Check if transaction has failed, only for Smart Transactions.
-	*/
+	 * Check if transaction has failed, only for Smart Transactions.
+	 */
 	function HasFailedTrans() {
 		if ($this->transOff > 0) {
 			return $this->_transOK == false;
@@ -1496,8 +1728,8 @@ if (!defined('_ADODB_LAYER')) {
 	}
 
 	/**
-	* @return # rows affected by UPDATE/DELETE
-	*/
+	 * @return int|false Number of rows affected by UPDATE/DELETE
+	 */
 	function Affected_Rows() {
 		if ($this->hasAffectedRows) {
 			if ($this->fnExecute === 'adodb_log_sql') {
@@ -1572,11 +1804,24 @@ if (!defined('_ADODB_LAYER')) {
 	}
 
 	/**
-	 * @returns assoc array where keys are tables, and values are foreign keys
+	 * Returns a list of Foreign Keys associated with a specific table.
+	 *
+	 * If there are no foreign keys then the function returns false.
+	 *
+	 * @param string $table       The name of the table to get the foreign keys for.
+	 * @param string $owner       Table owner/schema.
+	 * @param bool   $upper       If true, only matches the table with the uppercase name.
+	 * @param bool   $associative Returns the result in associative mode;
+	 *                            if ADODB_FETCH_MODE is already associative, then
+	 *                            this parameter is discarded.
+	 *
+	 * @return string[]|false An array where keys are tables, and values are foreign keys;
+	 *                        false if no foreign keys could be found.
 	 */
-	function MetaForeignKeys($table, $owner=false, $upper=false) {
+	function metaForeignKeys($table, $owner = '', $upper = false, $associative = false) {
 		return false;
 	}
+
 	/**
 	 * Choose a database to connect to. Many databases do not support this.
 	 *
@@ -1693,12 +1938,12 @@ if (!defined('_ADODB_LAYER')) {
 	}
 
 	/**
-	* Create serializable recordset. Breaks rs link to connection.
-	*
-	* @param ADORecordSet $rs the recordset to serialize
+	 * Create serializable recordset. Breaks rs link to connection.
 	 *
-	* @return ADORecordSet_array|bool the new recordset
-	*/
+	 * @param ADORecordSet $rs the recordset to serialize
+	 *
+	 * @return ADORecordSet_array|bool the new recordset
+	 */
 	function SerializableRS(&$rs) {
 		$rs2 = $this->_rs2rs($rs);
 		$ignore = false;
@@ -1708,17 +1953,17 @@ if (!defined('_ADODB_LAYER')) {
 	}
 
 	/**
-	* Convert a database recordset to an array recordset.
+	 * Convert a database recordset to an array recordset.
+	 *
+	 * Input recordset's cursor should be at beginning, and old $rs will be closed.
 	 *
-	* Input recordset's cursor should be at beginning, and old $rs will be closed.
-	*
 	 * @param ADORecordSet $rs     the recordset to copy
 	 * @param int          $nrows  number of rows to retrieve (optional)
 	 * @param int          $offset offset by number of rows (optional)
 	 * @param bool         $close
 	 *
 	 * @return ADORecordSet_array|ADORecordSet|bool the new recordset
-	*/
+	 */
 	function &_rs2rs(&$rs,$nrows=-1,$offset=-1,$close=true) {
 		if (! $rs) {
 			$ret = false;
@@ -1747,7 +1992,7 @@ if (!defined('_ADODB_LAYER')) {
 
 		$arrayClass = $this->arrayClass;
 
-		$rs2 = new $arrayClass();
+		$rs2 = new $arrayClass($fakeQueryId=1);
 		$rs2->connection = $this;
 		$rs2->sql = $rs->sql;
 		$rs2->dataProvider = $this->dataProvider;
@@ -1756,7 +2001,7 @@ if (!defined('_ADODB_LAYER')) {
 		return $rs2;
 	}
 
-	/*
+	/**
 	 * Return all rows.
 	 *
 	 * Compat with PEAR DB.
@@ -1765,9 +2010,9 @@ if (!defined('_ADODB_LAYER')) {
 	 * @param array|bool $inputarr Input bind array
 	 *
 	 * @return array|false
-	*/
+	 */
 	function GetAll($sql, $inputarr=false) {
-        return $this->GetArray($sql,$inputarr);
+		return $this->GetArray($sql,$inputarr);
 	}
 
 	/**
@@ -1941,23 +2186,17 @@ if (!defined('_ADODB_LAYER')) {
 		return $rv;
 	}
 
-	function Transpose(&$rs,$addfieldnames=true) {
-		$rs2 = $this->_rs2rs($rs);
-		if (!$rs2) {
-			return false;
-		}
-
-		$rs2->_transpose($addfieldnames);
-		return $rs2;
-	}
-
-	/*
-		Calculate the offset of a date for a particular database and generate
-			appropriate SQL. Useful for calculating future/past dates and storing
-			in a database.
-
-		If dayFraction=1.5 means 1.5 days from now, 1.0/24 for 1 hour.
-	*/
+	/**
+	 * Calculate the offset of a date for a particular database
+	 * and generate appropriate SQL.
+	 *
+	 * Useful for calculating future/past dates and storing in a database.
+	 *
+	 * @param double       $dayFraction 1.5 means 1.5 days from now, 1.0/24 for 1 hour
+	 * @param string|false $date        Reference date, false for system time
+	 *
+	 * @return string
+	 */
 	function OffsetDate($dayFraction,$date=false) {
 		if (!$date) {
 			$date = $this->sysDate;
@@ -2029,14 +2268,14 @@ if (!defined('_ADODB_LAYER')) {
 	}
 
 	/**
-	* Return one row of sql statement. Recordset is disposed for you.
-	* Note that SelectLimit should not be called.
-	*
-	* @param string     $sql      SQL statement
-	* @param array|bool $inputarr input bind array
+	 * Return one row of sql statement. Recordset is disposed for you.
+	 * Note that SelectLimit should not be called.
 	 *
-	* @return array|false Array containing the first row of the query
-	*/
+	 * @param string     $sql      SQL statement
+	 * @param array|bool $inputarr input bind array
+	 *
+	 * @return array|false Array containing the first row of the query
+	 */
 	function GetRow($sql,$inputarr=false) {
 		global $ADODB_COUNTRECS;
 
@@ -2083,24 +2322,24 @@ if (!defined('_ADODB_LAYER')) {
 	}
 
 	/**
-	* Insert or replace a single record. Note: this is not the same as MySQL's replace.
-	* ADOdb's Replace() uses update-insert semantics, not insert-delete-duplicates of MySQL.
-	* Also note that no table locking is done currently, so it is possible that the
-	* record be inserted twice by two programs...
-	*
-	* $this->Replace('products', array('prodname' =>"'Nails'","price" => 3.99), 'prodname');
-	*
-	* $table		table name
-	* $fieldArray	associative array of data (you must quote strings yourself).
-	* $keyCol		the primary key field name or if compound key, array of field names
-	* autoQuote		set to true to use a heuristic to quote strings. Works with nulls and numbers
-	*					but does not work with dates nor SQL functions.
-	* has_autoinc	the primary key is an auto-inc field, so skip in insert.
-	*
-	* Currently blob replace not supported
-	*
-	* returns 0 = fail, 1 = update, 2 = insert
-	*/
+	 * Insert or replace a single record. Note: this is not the same as MySQL's replace.
+	 * ADOdb's Replace() uses update-insert semantics, not insert-delete-duplicates of MySQL.
+	 * Also note that no table locking is done currently, so it is possible that the
+	 * record be inserted twice by two programs...
+	 *
+	 * $this->Replace('products', array('prodname' =>"'Nails'","price" => 3.99), 'prodname');
+	 *
+	 * $table		table name
+	 * $fieldArray	associative array of data (you must quote strings yourself).
+	 * $keyCol		the primary key field name or if compound key, array of field names
+	 * autoQuote		set to true to use a heuristic to quote strings. Works with nulls and numbers
+	 *					but does not work with dates nor SQL functions.
+	 * has_autoinc	the primary key is an auto-inc field, so skip in insert.
+	 *
+	 * Currently blob replace not supported
+	 *
+	 * returns 0 = fail, 1 = update, 2 = insert
+	 */
 
 	function Replace($table, $fieldArray, $keyCol, $autoQuote=false, $has_autoinc=false) {
 		global $ADODB_INCLUDED_LIB;
@@ -2457,37 +2696,47 @@ if (!defined('_ADODB_LAYER')) {
 
 
 	/**
-	* Update a blob column, given a where clause. There are more sophisticated
-	* blob handling functions that we could have implemented, but all require
-	* a very complex API. Instead we have chosen something that is extremely
-	* simple to understand and use.
-	*
-	* Note: $blobtype supports 'BLOB' and 'CLOB', default is BLOB of course.
-	*
-	* Usage to update a $blobvalue which has a primary key blob_id=1 into a
-	* field blobtable.blobcolumn:
-	*
-	*	UpdateBlob('blobtable', 'blobcolumn', $blobvalue, 'blob_id=1');
-	*
-	* Insert example:
-	*
-	*	$conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
-	*	$conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
-	*/
-	function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') {
+	 * Update a BLOB column, given a where clause.
+	 *
+	 * There are more sophisticated blob handling functions that we could have
+	 * implemented, but all require a very complex API. Instead we have chosen
+	 * something that is extremely simple to understand and use.
+	 *
+	 * Sample usage:
+	 * - update a BLOB in field table.blob_col with value $blobValue, for a
+	 *   record having primary key id=1
+	 *   $conn->updateBlob('table', 'blob_col', $blobValue, 'id=1');
+	 * - insert example:
+	 *   $conn->execute('INSERT INTO table (id, blob_col) VALUES (1, null)');
+	 *   $conn->updateBlob('table', 'blob_col', $blobValue, 'id=1');
+	 *
+	 * @param string $table
+	 * @param string $column
+	 * @param string $val      Filename containing blob data
+	 * @param mixed  $where    {@see updateBlob()}
+	 * @param string $blobtype supports 'BLOB' (default) and 'CLOB'
+	 *
+	 * @return bool success
+	 */
+	function updateBlob($table, $column, $val, $where, $blobtype='BLOB') {
 		return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false;
 	}
 
 	/**
-	* Usage:
-	*	UpdateBlob('TABLE', 'COLUMN', '/path/to/file', 'ID=1');
-	*
-	*	$blobtype supports 'BLOB' and 'CLOB'
-	*
-	*	$conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
-	*	$conn->UpdateBlob('blobtable','blobcol',$blobpath,'id=1');
-	*/
-	function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB') {
+	 * Update a BLOB from a file.
+	 *
+	 * Usage example:
+	 * $conn->updateBlobFile('table', 'blob_col', '/path/to/file', 'id=1');
+	 *
+	 * @param string $table
+	 * @param string $column
+	 * @param string $path     Filename containing blob data
+	 * @param mixed  $where    {@see updateBlob()}
+	 * @param string $blobtype supports 'BLOB' and 'CLOB'
+	 *
+	 * @return bool success
+	 */
+	function updateBlobFile($table, $column, $path, $where, $blobtype='BLOB') {
 		$fd = fopen($path,'rb');
 		if ($fd === false) {
 			return false;
@@ -2551,12 +2800,12 @@ if (!defined('_ADODB_LAYER')) {
 	}
 
 	/**
-	* Usage:
-	*	UpdateClob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB');
-	*
-	*	$conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)');
-	*	$conn->UpdateClob('clobtable','clobcol',$clob,'id=1');
-	*/
+	 * Usage:
+	 *	UpdateClob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB');
+	 *
+	 *	$conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)');
+	 *	$conn->UpdateClob('clobtable','clobcol',$clob,'id=1');
+	 */
 	function UpdateClob($table,$column,$val,$where) {
 		return $this->UpdateBlob($table,$column,$val,$where,'CLOB');
 	}
@@ -2575,9 +2824,9 @@ if (!defined('_ADODB_LAYER')) {
 
 
 	/**
-	*  Change the SQL connection locale to a specified locale.
-	*  This is used to get the date formats written depending on the client locale.
-	*/
+	 *  Change the SQL connection locale to a specified locale.
+	 *  This is used to get the date formats written depending on the client locale.
+	 */
 	function SetDateLocale($locale = 'En') {
 		$this->locale = $locale;
 		switch (strtoupper($locale))
@@ -2656,7 +2905,9 @@ if (!defined('_ADODB_LAYER')) {
 	}
 
 	/**
-	 * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans().
+	 * Begin a Transaction.
+	 *
+	 * Must be followed by CommitTrans() or RollbackTrans().
 	 *
 	 * @return bool true if succeeded or false if database does not support transactions
 	 */
@@ -2718,11 +2969,14 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 	}
 
 	/**
-	 * If database does not support transactions, always return true as data always committed
+	 * Commits a transaction.
 	 *
-	 * @param bool $ok  set to false to rollback transaction, true to commit
+	 * If database does not support transactions, return true as data is
+	 * always committed.
 	 *
-	 * @return true/false.
+	 * @param bool $ok True to commit, false to rollback the transaction.
+	 *
+	 * @return bool true if successful
 	 */
 	function CommitTrans($ok=true) {
 		return true;
@@ -2730,9 +2984,12 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 
 
 	/**
-	 * If database does not support transactions, rollbacks always fail, so return false
+	 * Rolls back a transaction.
 	 *
-	 * @return bool
+	 * If database does not support transactions, return false as rollbacks
+	 * always fail.
+	 *
+	 * @return bool true if successful
 	 */
 	function RollbackTrans() {
 		return false;
@@ -3269,17 +3526,17 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 
 
 	/**
-	* Will select the supplied $page number from a recordset, given that it is paginated in pages of
-	* $nrows rows per page. It also saves two boolean values saying if the given page is the first
-	* and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
-	*
-	* @param int $secs2cache	seconds to cache data, set to 0 to force query
-	* @param string $sql
-	* @param int $nrows		is the number of rows per page to get
-	* @param int $page		is the page number to get (1-based)
-	* @param mixed[]|bool $inputarr	array of bind variables
-	* @return mixed	the recordset ($rs->databaseType == 'array')
-	*/
+	 * Will select the supplied $page number from a recordset, given that it is paginated in pages of
+	 * $nrows rows per page. It also saves two boolean values saying if the given page is the first
+	 * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
+	 *
+	 * @param int $secs2cache	seconds to cache data, set to 0 to force query
+	 * @param string $sql
+	 * @param int $nrows		is the number of rows per page to get
+	 * @param int $page		is the page number to get (1-based)
+	 * @param mixed[]|bool $inputarr	array of bind variables
+	 * @return mixed	the recordset ($rs->databaseType == 'array')
+	 */
 	function CachePageExecute($secs2cache, $sql, $nrows, $page,$inputarr=false) {
 		/*switch($this->dataProvider) {
 		case 'postgres':
@@ -3291,37 +3548,37 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 	}
 
 	/**
-	* Returns the maximum size of a MetaType C field. If the method
-	* is not defined in the driver returns ADODB_STRINGMAX_NOTSET
-	*
-	* @return int
-	*/
+	 * Returns the maximum size of a MetaType C field. If the method
+	 * is not defined in the driver returns ADODB_STRINGMAX_NOTSET
+	 *
+	 * @return int
+	 */
 	function charMax() {
 		return ADODB_STRINGMAX_NOTSET;
 	}
 
 	/**
-	* Returns the maximum size of a MetaType X field. If the method
-	* is not defined in the driver returns ADODB_STRINGMAX_NOTSET
-	*
-	* @return int
-	*/
+	 * Returns the maximum size of a MetaType X field. If the method
+	 * is not defined in the driver returns ADODB_STRINGMAX_NOTSET
+	 *
+	 * @return int
+	 */
 	function textMax() {
 		return ADODB_STRINGMAX_NOTSET;
 	}
 
 	/**
-	* Returns a substring of a varchar type field
-	*
-	* Some databases have variations of the parameters, which is why
-	* we have an ADOdb function for it
-	*
-	* @param	string	$fld	The field to sub-string
-	* @param	int		$start	The start point
-	* @param	int		$length	An optional length
-	*
-	* @return string	The SQL text
-	*/
+	 * Returns a substring of a varchar type field
+	 *
+	 * Some databases have variations of the parameters, which is why
+	 * we have an ADOdb function for it
+	 *
+	 * @param	string	$fld	The field to sub-string
+	 * @param	int		$start	The start point
+	 * @param	int		$length	An optional length
+	 *
+	 * @return string	The SQL text
+	 */
 	function substr($fld,$start,$length=0) {
 		$text = "{$this->substr}($fld,$start";
 		if ($length > 0)
@@ -3426,15 +3683,14 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 	//==============================================================================================
 
 	/**
-	* Internal placeholder for record objects. Used by ADORecordSet->FetchObj().
-	*/
+	 * Internal placeholder for record objects. Used by ADORecordSet->FetchObj().
+	 */
 	class ADOFetchObj {
 	};
 
-	//==============================================================================================
-	// CLASS ADORecordSet_empty
-	//==============================================================================================
-
+	/**
+	 * Class ADODB_Iterator_empty
+	 */
 	class ADODB_Iterator_empty implements Iterator {
 
 		private $rs;
@@ -3477,8 +3733,8 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 
 
 	/**
-	* Lightweight recordset when there are no records to be returned
-	*/
+	 * Lightweight recordset when there are no records to be returned
+	 */
 	class ADORecordSet_empty implements IteratorAggregate
 	{
 		var $dataProvider = 'empty';
@@ -3564,10 +3820,9 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 		include_once(ADODB_DIR.'/adodb-time.inc.php');
 	}
 
-	//==============================================================================================
-	// CLASS ADORecordSet
-	//==============================================================================================
-
+	/**
+	 * Class ADODB_Iterator
+	 */
 	class ADODB_Iterator implements Iterator {
 
 		private $rs;
@@ -3612,13 +3867,14 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 	}
 
 
-	/**
-	 * RecordSet class that represents the dataset returned by the database.
-	 * To keep memory overhead low, this class holds only the current row in memory.
-	 * No prefetching of data is done, so the RecordCount() can return -1 ( which
-	 * means recordcount not known).
-	 */
-	class ADORecordSet implements IteratorAggregate {
+/**
+ * RecordSet class that represents the dataset returned by the database.
+ *
+ * To keep memory overhead low, this class holds only the current row in memory.
+ * No prefetching of data is done, so the RecordCount() can return -1 (which
+ * means recordcount not known).
+ */
+class ADORecordSet implements IteratorAggregate {
 
 	/**
 	 * public variables
@@ -3639,8 +3895,8 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 
 	var $bind = false;		/// used by Fields() to hold array - should be private?
 	var $fetchMode;			/// default fetch mode
-	var $connection = false; /// the parent connection
-
+	/** @var ADOConnection The parent connection */
+	var $connection = false;
 	/**
 	 *	private variables
 	 */
@@ -3661,6 +3917,10 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 	var $_maxRecordCount = 0;
 	var $datetime = false;
 
+	public $customActualTypes;
+	public $customMetaTypes;
+
+
 	/**
 	 * @var ADOFieldObject[] Field metadata cache
 	 * @see fieldTypesArray()
@@ -3670,10 +3930,10 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 	/**
 	 * Constructor
 	 *
-	 * @param resource|int queryID	this is the queryID returned by ADOConnection->_query()
-	 *
+	 * @param resource|int $queryID Query ID returned by ADOConnection->_query()
+	 * @param int|bool     $mode    The ADODB_FETCH_MODE value
 	 */
-	function __construct($queryID) {
+	function __construct($queryID,$mode=false) {
 		$this->_queryID = $queryID;
 	}
 
@@ -3698,7 +3958,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 		}
 		$this->_inited = true;
 		if ($this->_queryID) {
-			@$this->_initrs();
+			@$this->_initRS();
 		} else {
 			$this->_numOfRows = 0;
 			$this->_numOfFields = 0;
@@ -3713,6 +3973,16 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 		}
 	}
 
+	/**
+	 * Recordset initialization stub
+	 */
+	protected function _initRS() {}
+
+	/**
+	 * Row fetch stub
+	 * @return bool
+	 */
+	protected function _fetch() {}
 
 	/**
 	 * Generate a SELECT tag from a recordset, and return the HTML markup.
@@ -3851,24 +4121,28 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 		return $this->GetArray($nRows);
 	}
 
-	/*
-	* Some databases allow multiple recordsets to be returned. This function
-	* will return true if there is a next recordset, or false if no more.
-	*/
+	/**
+	 * Checks if there is another available recordset.
+	 *
+	 * Some databases allow multiple recordsets to be returned.
+	 *
+	 * @return boolean true if there is a next recordset, or false if no more
+	 */
 	function NextRecordSet() {
 		return false;
 	}
 
 	/**
-	 * return recordset as a 2-dimensional array.
+	 * Return recordset as a 2-dimensional array.
+	 *
 	 * Helper function for ADOConnection->SelectLimit()
 	 *
-	 * @param offset	is the row to start calculations from (1-based)
-	 * @param [nrows]	is the number of rows to return
+	 * @param int $nrows  Number of rows to return
+	 * @param int $offset Starting row (1-based)
 	 *
 	 * @return array an array indexed by the rows (0-based) from the recordset
 	 */
-	function GetArrayLimit($nrows,$offset=-1) {
+	function getArrayLimit($nrows, $offset=-1) {
 		if ($offset <= 0) {
 			return $this->GetArray($nrows);
 		}
@@ -3889,11 +4163,11 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 	/**
 	 * Synonym for GetArray() for compatibility with ADO.
 	 *
-	 * @param [nRows]  is the number of rows to return. -1 means every row.
+	 * @param int $nRows Number of rows to return. -1 means every row.
 	 *
 	 * @return array an array indexed by the rows (0-based) from the recordset
 	 */
-	function GetRows($nRows = -1) {
+	function getRows($nRows = -1) {
 		return $this->GetArray($nRows);
 	}
 
@@ -4106,8 +4380,8 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 
 
 	/**
-	* PEAR DB Compat - do not use internally
-	*/
+	 * PEAR DB Compat - do not use internally
+	 */
 	function Free() {
 		return $this->Close();
 	}
@@ -4152,13 +4426,13 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 
 
 	/**
-	* Fetch a row, returning PEAR_Error if no more rows.
-	* This is PEAR DB compat mode.
-	*
-	* @param mixed[]|false $arr
-	*
-	* @return mixed DB_OK or error object
-	*/
+	 * Fetch a row, returning PEAR_Error if no more rows.
+	 * This is PEAR DB compat mode.
+	 *
+	 * @param mixed[]|false $arr
+	 *
+	 * @return mixed DB_OK or error object
+	 */
 	function FetchInto(&$arr) {
 		if ($this->EOF) {
 			return (defined('PEAR_ERROR_RETURN')) ? new PEAR_Error('EOF',-1): false;
@@ -4281,6 +4555,14 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 		return false;
 	}
 
+	/**
+	 * Adjusts the result pointer to an arbitrary row in the result.
+	 *
+	 * @param int $row The row to seek to.
+	 *
+	 * @return bool False if the recordset contains no rows, otherwise true.
+	 */
+	function _seek($row) {}
 
 	/**
 	 * Get the value of a field in the current row by column name.
@@ -4354,8 +4636,9 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 	 * Use associative array to get fields array for databases that do not support
 	 * associative arrays. Submitted by Paolo S. Asioli paolo.asioli#libero.it
 	 *
-	 * @param int [$upper] Case for the array keys, defaults to uppercase
+	 * @param int $upper Case for the array keys, defaults to uppercase
 	 *                   (see ADODB_ASSOC_CASE_xxx constants)
+	 * @return array
 	 */
 	function GetRowAssoc($upper = ADODB_ASSOC_CASE) {
 		$record = array();
@@ -4391,15 +4674,14 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 	}
 
 	/**
-	 * Synonyms RecordCount and RowCount
+	 * Number of rows in recordset.
 	 *
 	 * @return int Number of rows or -1 if this is not supported
 	 */
-	function RecordCount() {
+	function recordCount() {
 		return $this->_numOfRows;
 	}
 
-
 	/**
 	 * If we are using PageExecute(), this will return the maximum possible rows
 	 * that can be returned when paging a recordset.
@@ -4407,26 +4689,32 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 	 * @return int
 	 */
 	function MaxRecordCount() {
-		return ($this->_maxRecordCount) ? $this->_maxRecordCount : $this->RecordCount();
+		return ($this->_maxRecordCount) ? $this->_maxRecordCount : $this->recordCount();
 	}
 
 	/**
-	 * synonyms RecordCount and RowCount
+	 * Number of rows in recordset.
+	 * Alias for {@see recordCount()}
 	 *
-	 * @return the number of rows or -1 if this is not supported
+	 * @return int Number of rows or -1 if this is not supported
 	 */
-	function RowCount() {
-		return $this->_numOfRows;
+	function rowCount() {
+		return $this->recordCount();
 	}
 
-
-	 /**
-	 * Portable RecordCount. Pablo Roca 
+	/**
+	 * Portable RecordCount.
 	 *
-	 * @return  the number of records from a previous SELECT. All databases support this.
+	 * Be aware of possible problems in multiuser environments.
+	 * For better speed the table must be indexed by the condition.
+	 * Heavy test this before deploying.
 	 *
-	 * But aware possible problems in multiuser environments. For better speed the table
-	 * must be indexed by the condition. Heavy test this before deploying.
+	 * @param string $table
+	 * @param string $condition
+	 *
+	 * @return int Number of records from a previous SELECT. All databases support this.
+	 *
+	 * @author Pablo Roca 
 	 */
 	function PO_RecordCount($table="", $condition="") {
 
@@ -4500,24 +4788,24 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 	}
 
 	/**
-	* Return the fields array of the current row as an object for convenience.
-	* The default case is lowercase field names.
-	*
-	* @return the object with the properties set to the fields of the current row
-	*/
+	 * Return the fields array of the current row as an object for convenience.
+	 * The default case is lowercase field names.
+	 *
+	 * @return the object with the properties set to the fields of the current row
+	 */
 	function FetchObj() {
 		return $this->FetchObject(false);
 	}
 
 	/**
-	* Return the fields array of the current row as an object for convenience.
-	* The default case is uppercase.
-	*
-	* @param $isupper to set the object property names to uppercase
-	*
-	* @return the object with the properties set to the fields of the current row
-	*/
-	function FetchObject($isupper=true) {
+	 * Return the fields array of the current row as an object for convenience.
+	 * The default case is uppercase.
+	 *
+	 * @param bool $isUpper to set the object property names to uppercase
+	 *
+	 * @return ADOFetchObj The object with properties set to the fields of the current row
+	 */
+	function FetchObject($isUpper=true) {
 		if (empty($this->_obj)) {
 			$this->_obj = new ADOFetchObj();
 			$this->_names = array();
@@ -4526,12 +4814,11 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 				$this->_names[] = $f->name;
 			}
 		}
-		$i = 0;
 		$o = clone($this->_obj);
 
 		for ($i=0; $i <$this->_numOfFields; $i++) {
 			$name = $this->_names[$i];
-			if ($isupper) {
+			if ($isUpper) {
 				$n = strtoupper($name);
 			} else {
 				$n = $name;
@@ -4543,34 +4830,34 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 	}
 
 	/**
-	* Return the fields array of the current row as an object for convenience.
-	* The default is lower-case field names.
-	*
-	* @return the object with the properties set to the fields of the current row,
-	*	or false if EOF
-	*
-	* Fixed bug reported by tim@orotech.net
-	*/
+	 * Return the fields array of the current row as an object for convenience.
+	 * The default is lower-case field names.
+	 *
+	 * @return ADOFetchObj|false The object with properties set to the fields of the current row
+	 *                           or false if EOF.
+	 *
+	 * Fixed bug reported by tim@orotech.net
+	 */
 	function FetchNextObj() {
 		return $this->FetchNextObject(false);
 	}
 
 
 	/**
-	* Return the fields array of the current row as an object for convenience.
-	* The default is upper case field names.
-	*
-	* @param $isupper to set the object property names to uppercase
-	*
-	* @return the object with the properties set to the fields of the current row,
-	*	or false if EOF
-	*
-	* Fixed bug reported by tim@orotech.net
-	*/
-	function FetchNextObject($isupper=true) {
+	 * Return the fields array of the current row as an object for convenience.
+	 * The default is upper case field names.
+	 *
+	 * @param bool $isUpper to set the object property names to uppercase
+	 *
+	 * @return ADOFetchObj|false The object with properties set to the fields of the current row
+	 *                           or false if EOF.
+	 *
+	 * Fixed bug reported by tim@orotech.net
+	 */
+	function FetchNextObject($isUpper=true) {
 		$o = false;
 		if ($this->_numOfRows != 0 && !$this->EOF) {
-			$o = $this->FetchObject($isupper);
+			$o = $this->FetchObject($isUpper);
 			$this->_currentRow++;
 			if ($this->_fetch()) {
 				return $o;
@@ -4581,37 +4868,29 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 	}
 
 	/**
-	 * Get the metatype of the column. This is used for formatting. This is because
-	 * many databases use different names for the same type, so we transform the original
-	 * type to our standardised version which uses 1 character codes:
+	 * Get the ADOdb metatype.
 	 *
-	 * @param t  is the type passed in. Normally is ADOFieldObject->type.
-	 * @param len is the maximum length of that field. This is because we treat character
-	 *	fields bigger than a certain size as a 'B' (blob).
-	 * @param fieldobj is the field object returned by the database driver. Can hold
-	 *	additional info (eg. primary_key for mysql).
+	 * Many databases use different names for the same type, so we transform
+	 * the native type to our standardised one, which uses 1 character codes.
+	 * @see https://adodb.org/dokuwiki/doku.php?id=v5:dictionary:dictionary_index#portable_data_types
 	 *
-	 * @return the general type of the data:
-	 *	C for character < 250 chars
-	 *	X for teXt (>= 250 chars)
-	 *	B for Binary
-	 *	N for numeric or floating point
-	 *	D for date
-	 *	T for timestamp
-	 *	L for logical/Boolean
-	 *	I for integer
-	 *	R for autoincrement counter/integer
+	 * @param string|ADOFieldObject $t  Native type (usually ADOFieldObject->type)
+	 *                                  It is also possible to provide an
+	 *                                  ADOFieldObject here.
+	 * @param int $len The field's maximum length. This is because we treat
+	 *                 character fields bigger than a certain size as a 'B' (blob).
+	 * @param ADOFieldObject $fieldObj Field object returned by the database driver;
+	 *                                 can hold additional info (eg. primary_key for mysql).
 	 *
-	 *
-	*/
-	function MetaType($t,$len=-1,$fieldobj=false) {
-		if (is_object($t)) {
-			$fieldobj = $t;
-			$t = $fieldobj->type;
-			$len = $fieldobj->max_length;
+	 * @return string The ADOdb Standard type
+	 */
+	function metaType($t, $len = -1, $fieldObj = false) {
+		if ($t instanceof ADOFieldObject) {
+			$fieldObj = $t;
+			$t = $fieldObj->type;
+			$len = $fieldObj->max_length;
 		}
 
-
 		// changed in 2.32 to hashing instead of switch stmt for speed...
 		static $typeMap = array(
 			'VARCHAR' => 'C',
@@ -4718,8 +4997,6 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 			"SQLBOOL" => 'L'
 		);
 
-
-		$tmap = false;
 		$t = strtoupper($t);
 		$tmap = (isset($typeMap[$t])) ? $typeMap[$t] : ADODB_DEFAULT_METATYPE;
 		switch ($tmap) {
@@ -4735,7 +5012,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 				return 'C';
 
 			case 'I':
-				if (!empty($fieldobj->primary_key)) {
+				if (!empty($fieldObj->primary_key)) {
 					return 'R';
 				}
 				return 'I';
@@ -4744,8 +5021,8 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 				return 'N';
 
 			case 'B':
-				if (isset($fieldobj->binary)) {
-					return ($fieldobj->binary) ? 'B' : 'X';
+				if (isset($fieldObj->binary)) {
+					return ($fieldObj->binary) ? 'B' : 'X';
 				}
 				return 'B';
 
@@ -4796,8 +5073,10 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 
 	/**
 	 * set/returns the current recordset page when paginating
+	 * @param int $page
+	 * @return int
 	 */
-	function AbsolutePage($page=-1) {
+	function absolutePage($page=-1) {
 		if ($page != -1) {
 			$this->_currentPage = $page;
 		}
@@ -4806,6 +5085,8 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 
 	/**
 	 * set/returns the status of the atFirstPage flag when paginating
+	 * @param bool $status
+	 * @return bool
 	 */
 	function AtFirstPage($status=false) {
 		if ($status != false) {
@@ -4814,6 +5095,10 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 		return $this->_atFirstPage;
 	}
 
+	/**
+	 * @param bool $page
+	 * @return bool
+	 */
 	function LastPageNo($page = false) {
 		if ($page != false) {
 			$this->_lastPageNo = $page;
@@ -4823,6 +5108,8 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 
 	/**
 	 * set/returns the status of the atLastPage flag when paginating
+	 * @param bool $status
+	 * @return bool
 	 */
 	function AtLastPage($status=false) {
 		if ($status != false) {
@@ -4860,48 +5147,22 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 
 		/**
 		 * Constructor
+		 *
+		 * The parameters passed to this recordset are always fake because
+		 * this class does not use the queryID
+		 *
+		 * @param resource|int $queryID Ignored
+		 * @param int|bool     $mode    The ADODB_FETCH_MODE value
 		 */
-		function __construct($fakeid=1) {
+		function __construct($queryID, $mode=false) {
 			global $ADODB_FETCH_MODE,$ADODB_COMPAT_FETCH;
 
 			// fetch() on EOF does not delete $this->fields
 			$this->compat = !empty($ADODB_COMPAT_FETCH);
-			parent::__construct($fakeid); // fake queryID
+			parent::__construct($queryID); // fake queryID
 			$this->fetchMode = $ADODB_FETCH_MODE;
 		}
 
-		function _transpose($addfieldnames=true) {
-			global $ADODB_INCLUDED_LIB;
-
-			if (empty($ADODB_INCLUDED_LIB)) {
-				include_once(ADODB_DIR.'/adodb-lib.inc.php');
-			}
-			$hdr = true;
-
-			$fobjs = $addfieldnames ? $this->_fieldobjects : false;
-			adodb_transpose($this->_array, $newarr, $hdr, $fobjs);
-			//adodb_pr($newarr);
-
-			$this->_skiprow1 = false;
-			$this->_array = $newarr;
-			$this->_colnames = $hdr;
-
-			adodb_probetypes($newarr,$this->_types);
-
-			$this->_fieldobjects = array();
-
-			foreach($hdr as $k => $name) {
-				$f = new ADOFieldObject();
-				$f->name = $name;
-				$f->type = $this->_types[$k];
-				$f->max_length = -1;
-				$this->_fieldobjects[] = $f;
-			}
-			$this->fields = reset($this->_array);
-
-			$this->_initrs();
-
-		}
 
 		/**
 		 * Setup the array.
@@ -5115,12 +5376,8 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 				break;
 
 			case 'mysql':
-				// mysql driver deprecated since 5.5, removed in 7.0
-				// automatically switch to mysqli
-				if(version_compare(PHP_VERSION, '7.0.0', '>=')) {
-					$db = 'mysqli';
-				}
-				$class = $db;
+				// mysql extension removed in PHP 7.0 - automatically switch to mysqli
+				$class = $db = 'mysqli';
 				break;
 
 			default:
@@ -5497,9 +5754,9 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 		return $dict;
 	}
 
-	/*
-		Perform a print_r, with pre tags for better formatting.
-	*/
+	/**
+	 * Perform a print_r, with pre tags for better formatting.
+	 */
 	function adodb_pr($var,$as_string=false) {
 		if ($as_string) {
 			ob_start();
@@ -5518,12 +5775,15 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1
 		}
 	}
 
-	/*
-		Perform a stack-crawl and pretty print it.
-
-		@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
-	*/
+	/**
+	 * Perform a stack-crawl and pretty print it.
+	 *
+	 * @param bool  $printOrArr Pass in a boolean to indicate print, or an $exception->trace array (assumes that print is true then).
+	 * @param int   $levels     Number of levels to display
+	 * @param mixed $ishtml
+	 *
+	 * @return string
+	 */
 	function adodb_backtrace($printOrArr=true,$levels=9999,$ishtml=null) {
 		global $ADODB_INCLUDED_LIB;
 		if (empty($ADODB_INCLUDED_LIB)) {
diff --git a/lib/adodb/datadict/datadict-access.inc.php b/lib/adodb/datadict/datadict-access.inc.php
index 7a7d4cbbdc8..b3f9fad0a2e 100644
--- a/lib/adodb/datadict/datadict-access.inc.php
+++ b/lib/adodb/datadict/datadict-access.inc.php
@@ -30,6 +30,15 @@ class ADODB2_access extends ADODB_DataDict {
 
  	function ActualType($meta)
 	{
+		$meta = strtoupper($meta);
+		
+		/*
+		* Add support for custom meta types. We do this
+		* first, that allows us to override existing types
+		*/
+		if (isset($this->connection->customMetaTypes[$meta]))
+			return $this->connection->customMetaTypes[$meta]['actual'];
+		
 		switch($meta) {
 		case 'C': return 'TEXT';
 		case 'XL':
@@ -41,18 +50,19 @@ class ADODB2_access extends ADODB_DataDict {
 		case 'B': return 'BINARY';
 
 		case 'TS':
-		case 'D': return 'DATETIME';
+		case 'D': 
+		return 'DATETIME';
 		case 'T': return 'DATETIME';
 
-		case 'L': return 'BYTE';
-		case 'I': return 'INTEGER';
+		case 'L':  return 'BYTE';
+		case 'I':  return 'INTEGER';
 		case 'I1': return 'BYTE';
 		case 'I2': return 'SMALLINT';
 		case 'I4': return 'INTEGER';
 		case 'I8': return 'INTEGER';
 
-		case 'F': return 'DOUBLE';
-		case 'N': return 'NUMERIC';
+		case 'F':  return 'DOUBLE';
+		case 'N':  return 'NUMERIC';
 		default:
 			return $meta;
 		}
diff --git a/lib/adodb/datadict/datadict-db2.inc.php b/lib/adodb/datadict/datadict-db2.inc.php
index c5dda09fb6b..9ac106bbbed 100644
--- a/lib/adodb/datadict/datadict-db2.inc.php
+++ b/lib/adodb/datadict/datadict-db2.inc.php
@@ -34,6 +34,15 @@ class ADODB2_db2 extends ADODB_DataDict {
 	
  	function ActualType($meta)
 	{
+		$meta = strtoupper($meta);
+		
+		/*
+		* Add support for custom meta types. We do this
+		* first, that allows us to override existing types
+		*/
+		if (isset($this->connection->customMetaTypes[$meta]))
+			return $this->connection->customMetaTypes[$meta]['actual'];
+		
 		switch($meta) {
 		case 'C': return 'VARCHAR';
 		case 'XL': return 'CLOB';
diff --git a/lib/adodb/datadict/datadict-firebird.inc.php b/lib/adodb/datadict/datadict-firebird.inc.php
index 0020a0ae0b4..79d0a8f5c34 100644
--- a/lib/adodb/datadict/datadict-firebird.inc.php
+++ b/lib/adodb/datadict/datadict-firebird.inc.php
@@ -22,8 +22,8 @@
 // security - hide paths
 if (!defined('ADODB_DIR')) die();
 
-class ADODB2_firebird extends ADODB_DataDict {
-
+class ADODB2_firebird extends ADODB_DataDict
+{
 	var $databaseType = 'firebird';
 	var $seqField = false;
 	var $seqPrefix = 's_';
@@ -32,69 +32,92 @@ class ADODB2_firebird extends ADODB_DataDict {
 	var $alterCol = ' ALTER';
 	var $dropCol = ' DROP';
 
-	function ActualType($meta)
+	function actualType($meta)
 	{
+
+		$meta = strtoupper($meta);
+
+		// Add support for custom meta types.
+		// We do this first, that allows us to override existing types
+		if (isset($this->connection->customMetaTypes[$meta])) {
+			return $this->connection->customMetaTypes[$meta]['actual'];
+		}
+
 		switch($meta) {
-		case 'C': return 'VARCHAR';
-		case 'XL':
-		case 'X': return 'BLOB SUB_TYPE TEXT';
+			case 'C':
+				return 'VARCHAR';
+			case 'XL':
+				return 'BLOB SUB_TYPE BINARY';
+			case 'X':
+				return 'BLOB SUB_TYPE TEXT';
 
-		case 'C2': return 'VARCHAR(32765)'; // up to 32K
-		case 'X2': return 'VARCHAR(4096)';
+			case 'C2':
+				return 'VARCHAR(32765)'; // up to 32K
+			case 'X2':
+				return 'VARCHAR(4096)';
 
-		case 'V': return 'CHAR';
-		case 'C1': return 'CHAR(1)';
+			case 'V':
+				return 'CHAR';
+			case 'C1':
+				return 'CHAR(1)';
 
-		case 'B': return 'BLOB';
+			case 'B':
+				return 'BLOB';
 
-		case 'D': return 'DATE';
-		case 'TS':
-		case 'T': return 'TIMESTAMP';
+			case 'D':
+				return 'DATE';
+			case 'TS':
+			case 'T':
+				return 'TIMESTAMP';
 
-		case 'L': return 'SMALLINT';
-		case 'I': return 'INTEGER';
-		case 'I1': return 'SMALLINT';
-		case 'I2': return 'SMALLINT';
-		case 'I4': return 'INTEGER';
-		case 'I8': return 'BIGINT';
+			case 'L':
+			case 'I1':
+			case 'I2':
+				return 'SMALLINT';
+			case 'I':
+			case 'I4':
+				return 'INTEGER';
+			case 'I8':
+				return 'BIGINT';
 
-		case 'F': return 'DOUBLE PRECISION';
-		case 'N': return 'DECIMAL';
-		default:
-			return $meta;
+			case 'F':
+				return 'DOUBLE PRECISION';
+			case 'N':
+				return 'DECIMAL';
+			default:
+				return $meta;
 		}
 	}
 
-	function NameQuote($name = NULL,$allowBrackets=false)
+	function nameQuote($name = null, $allowBrackets = false)
 	{
 		if (!is_string($name)) {
-			return FALSE;
+			return false;
 		}
 
 		$name = trim($name);
 
-		if ( !is_object($this->connection) ) {
+		if (!is_object($this->connection)) {
 			return $name;
 		}
 
 		$quote = $this->connection->nameQuote;
 
 		// if name is of the form `name`, quote it
-		if ( preg_match('/^`(.+)`$/', $name, $matches) ) {
+		if (preg_match('/^`(.+)`$/', $name, $matches)) {
 			return $quote . $matches[1] . $quote;
 		}
 
 		// if name contains special characters, quote it
-		if ( !preg_match('/^[' . $this->nameRegex . ']+$/', $name) ) {
+		if (!preg_match('/^[' . $this->nameRegex . ']+$/', $name)) {
 			return $quote . $name . $quote;
 		}
 
 		return $quote . $name . $quote;
 	}
 
-	function CreateDatabase($dbname, $options=false)
+	function createDatabase($dbname, $options = false)
 	{
-		$options = $this->_Options($options);
 		$sql = array();
 
 		$sql[] = "DECLARE EXTERNAL FUNCTION LOWER CSTRING(80) RETURNS CSTRING(80) FREE_IT ENTRY_POINT 'IB_UDF_lower' MODULE_NAME 'ib_udf'";
@@ -102,50 +125,62 @@ class ADODB2_firebird extends ADODB_DataDict {
 		return $sql;
 	}
 
-	function _DropAutoIncrement($t)
+	function _dropAutoIncrement($tabname)
 	{
-		if (strpos($t,'.') !== false) {
-			$tarr = explode('.',$t);
-			return 'DROP GENERATOR '.$tarr[0].'."s_'.$tarr[1].'"';
+		if (strpos($tabname, '.') !== false) {
+			$tarr = explode('.', $tabname);
+			return 'DROP SEQUENCE ' . $tarr[0] . '."s_' . $tarr[1] . '"';
 		}
-		return 'DROP GENERATOR s_'.$t;
+		return 'DROP SEQUENCE s_' . $tabname;
 	}
 
 
-	function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
+	function _createSuffix($fname, &$ftype, $fnotnull, $fdefault, $fautoinc, $fconstraint, $funsigned)
 	{
 		$suffix = '';
 
-		if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault";
-		if ($fnotnull) $suffix .= ' NOT NULL';
-		if ($fautoinc) $this->seqField = $fname;
+		if (strlen($fdefault)) {
+			$suffix .= " DEFAULT $fdefault";
+		}
+		if ($fnotnull) {
+			$suffix .= ' NOT NULL';
+		}
+		if ($fautoinc) {
+			$this->seqField = $fname;
+		}
 		$fconstraint = preg_replace("/``/", "\"", $fconstraint);
-		if ($fconstraint) $suffix .= ' '.$fconstraint;
+		if ($fconstraint) {
+			$suffix .= ' ' . $fconstraint;
+		}
 
 		return $suffix;
 	}
 
 	/**
-	 Generate the SQL to create table. Returns an array of sql strings.
-	*/
-	function CreateTableSQL($tabname, $flds, $tableoptions=array())
+	 * Generate the SQL to create table. Returns an array of sql strings.
+	 */
+	function createTableSQL($tabname, $flds, $tableoptions = array())
 	{
-		list($lines,$pkey,$idxs) = $this->_GenFields($flds, true);
+		list($lines, $pkey, $idxs) = $this->_GenFields($flds, true);
 		// genfields can return FALSE at times
-		if ($lines == null) $lines = array();
+		if ($lines == null) {
+			$lines = array();
+		}
 
 		$taboptions = $this->_Options($tableoptions);
-		$tabname = $this->TableName ($tabname);
-		$sql = $this->_TableSQL($tabname,$lines,$pkey,$taboptions);
+		$tabname = $this->TableName($tabname);
+		$sql = $this->_TableSQL($tabname, $lines, $pkey, $taboptions);
 
-		if ($this->autoIncrement && !isset($taboptions['DROP']))
-		{ $tsql = $this->_Triggers($tabname,$taboptions);
-			foreach($tsql as $s) $sql[] = $s;
+		if ($this->autoIncrement && !isset($taboptions['DROP'])) {
+			$tsql = $this->_Triggers($tabname, $taboptions);
+			foreach ($tsql as $s) {
+				$sql[] = $s;
+			}
 		}
 
 		if (is_array($idxs)) {
-			foreach($idxs as $idx => $idxdef) {
-				$sql_idxs = $this->CreateIndexSql($idx, $tabname,  $idxdef['cols'], $idxdef['opts']);
+			foreach ($idxs as $idx => $idxdef) {
+				$sql_idxs = $this->CreateIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']);
 				$sql = array_merge($sql, $sql_idxs);
 			}
 		}
@@ -154,44 +189,47 @@ class ADODB2_firebird extends ADODB_DataDict {
 	}
 
 
-/*
-CREATE or replace TRIGGER jaddress_insert
-before insert on jaddress
-for each row
-begin
-IF ( NEW."seqField" IS NULL OR NEW."seqField" = 0 ) THEN
-  NEW."seqField" = GEN_ID("GEN_tabname", 1);
-end;
-*/
-	function _Triggers($tabname,$tableoptions)
+	/*
+	CREATE or replace TRIGGER jaddress_insert
+	before insert on jaddress
+	for each row
+	begin
+	IF ( NEW."seqField" IS NULL OR NEW."seqField" = 0 ) THEN
+	  NEW."seqField" = GEN_ID("GEN_tabname", 1);
+	end;
+	*/
+	function _triggers($tabname, $taboptions)
 	{
-		if (!$this->seqField) return array();
+		if (!$this->seqField) {
+			return array();
+		}
 
-		$tab1 = preg_replace( '/"/', '', $tabname );
+		$tab1 = preg_replace('/"/', '', $tabname);
 		if ($this->schema) {
-			$t = strpos($tab1,'.');
-			if ($t !== false) $tab = substr($tab1,$t+1);
-			else $tab = $tab1;
+			$t = strpos($tab1, '.');
+			if ($t !== false) {
+				$tab = substr($tab1, $t + 1);
+			} else {
+				$tab = $tab1;
+			}
 			$seqField = $this->seqField;
-			$seqname = $this->schema.'.'.$this->seqPrefix.$tab;
-			$trigname = $this->schema.'.t_'.$this->seqPrefix.$tab;
+			$seqname = $this->schema . '.' . $this->seqPrefix . $tab;
+			$trigname = $this->schema . '.t_' . $this->seqPrefix . $tab;
 		} else {
 			$seqField = $this->seqField;
-			$seqname = $this->seqPrefix.$tab1;
-			$trigname = 't_'.$seqname;
+			$seqname = $this->seqPrefix . $tab1;
+			$trigname = 't_' . $seqname;
 		}
 
-		if (isset($tableoptions['DROP']))
-		{ $sql[] = "DROP GENERATOR $seqname";
-		}
-		elseif (isset($tableoptions['REPLACE']))
-		{ $sql[] = "DROP GENERATOR \"$seqname\"";
-		  $sql[] = "CREATE GENERATOR \"$seqname\"";
-		  $sql[] = "ALTER TRIGGER \"$trigname\" BEFORE INSERT OR UPDATE AS BEGIN IF ( NEW.$seqField IS NULL OR NEW.$seqField = 0 ) THEN NEW.$seqField = GEN_ID(\"$seqname\", 1); END";
-		}
-		else
-		{ $sql[] = "CREATE GENERATOR $seqname";
-		  $sql[] = "CREATE TRIGGER $trigname FOR $tabname BEFORE INSERT OR UPDATE AS BEGIN IF ( NEW.$seqField IS NULL OR NEW.$seqField = 0 ) THEN NEW.$seqField = GEN_ID($seqname, 1); END";
+		if (isset($taboptions['DROP'])) {
+			$sql[] = "DROP SEQUENCE $seqname";
+		} elseif (isset($taboptions['REPLACE'])) {
+			$sql[] = "DROP SEQUENCE \"$seqname\"";
+			$sql[] = "CREATE SEQUENCE \"$seqname\"";
+			$sql[] = "ALTER TRIGGER \"$trigname\" BEFORE INSERT OR UPDATE AS BEGIN IF ( NEW.$seqField IS NULL OR NEW.$seqField = 0 ) THEN NEW.$seqField = GEN_ID(\"$seqname\", 1); END";
+		} else {
+			$sql[] = "CREATE SEQUENCE $seqname";
+			$sql[] = "CREATE TRIGGER $trigname FOR $tabname BEFORE INSERT OR UPDATE AS BEGIN IF ( NEW.$seqField IS NULL OR NEW.$seqField = 0 ) THEN NEW.$seqField = GEN_ID($seqname, 1); END";
 		}
 
 		$this->seqField = false;
@@ -201,27 +239,39 @@ end;
 	/**
 	 * Change the definition of one column
 	 *
-	 * As some DBM's can't do that on there own, you need to supply the complete definition of the new table,
-	 * to allow, recreating the table and copying the content over to the new table
 	 * @param string $tabname table-name
 	 * @param string $flds column-name and type for the changed column
-	 * @param string $tableflds='' complete definition of the new table, eg. for postgres, default ''
-	 * @param array/string $tableoptions='' options for the new table see CreateTableSQL, default ''
+	 * @param string $tableflds Unused
+	 * @param array|string $tableoptions Unused
+	 *
 	 * @return array with SQL strings
 	 */
-	function AlterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='')
+	public function alterColumnSQL($tabname, $flds, $tableflds = '', $tableoptions = '')
 	{
-		$tabname = $this->TableName ($tabname);
+		$tabname = $this->TableName($tabname);
 		$sql = array();
-		list($lines,$pkey,$idxs) = $this->_GenFields($flds);
+		list($lines, , $idxs) = $this->_GenFields($flds);
 		// genfields can return FALSE at times
-		if ($lines == null) $lines = array();
+
+		if ($lines == null) {
+			$lines = array();
+		}
+
 		$alter = 'ALTER TABLE ' . $tabname . $this->alterCol . ' ';
-		foreach($lines as $v) {
+
+		foreach ($lines as $v) {
+			/*
+			* The type must be preceded by the keyword 'TYPE'
+			*/
+			$vExplode = explode(' ', $v);
+			$vExplode = array_filter($vExplode);
+			array_splice($vExplode, 1, 0, array('TYPE'));
+			$v = implode(' ', $vExplode);
 			$sql[] = $alter . $v;
 		}
+
 		if (is_array($idxs)) {
-			foreach($idxs as $idx => $idxdef) {
+			foreach ($idxs as $idx => $idxdef) {
 				$sql_idxs = $this->CreateIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']);
 				$sql = array_merge($sql, $sql_idxs);
 			}
diff --git a/lib/adodb/datadict/datadict-generic.inc.php b/lib/adodb/datadict/datadict-generic.inc.php
index c9c8dee871f..1a60dbc2466 100644
--- a/lib/adodb/datadict/datadict-generic.inc.php
+++ b/lib/adodb/datadict/datadict-generic.inc.php
@@ -28,8 +28,19 @@ class ADODB2_generic extends ADODB_DataDict {
 	var $seqField = false;
 
 
+
  	function ActualType($meta)
 	{
+		
+		$meta = strtoupper($meta);
+		
+		/*
+		* Add support for custom meta types. We do this
+		* first, that allows us to override existing types
+		*/
+		if (isset($this->connection->customMetaTypes[$meta]))
+			return $this->connection->customMetaTypes[$meta]['actual'];
+		
 		switch($meta) {
 		case 'C': return 'VARCHAR';
 		case 'XL':
diff --git a/lib/adodb/datadict/datadict-ibase.inc.php b/lib/adodb/datadict/datadict-ibase.inc.php
index 5f58880ff5e..4310ded4b36 100644
--- a/lib/adodb/datadict/datadict-ibase.inc.php
+++ b/lib/adodb/datadict/datadict-ibase.inc.php
@@ -30,6 +30,15 @@ class ADODB2_ibase extends ADODB_DataDict {
 
  	function ActualType($meta)
 	{
+		$meta = strtoupper($meta);
+		
+		/*
+		* Add support for custom meta types. We do this
+		* first, that allows us to override existing types
+		*/
+		if (isset($this->connection->customMetaTypes[$meta]))
+			return $this->connection->customMetaTypes[$meta]['actual'];
+		
 		switch($meta) {
 		case 'C': return 'VARCHAR';
 		case 'XL':
diff --git a/lib/adodb/datadict/datadict-informix.inc.php b/lib/adodb/datadict/datadict-informix.inc.php
index acb5ba74ce6..9e151633fdf 100644
--- a/lib/adodb/datadict/datadict-informix.inc.php
+++ b/lib/adodb/datadict/datadict-informix.inc.php
@@ -30,6 +30,15 @@ class ADODB2_informix extends ADODB_DataDict {
 
 	function ActualType($meta)
 	{
+		$meta = strtoupper($meta);
+		
+		/*
+		* Add support for custom meta types. We do this
+		* first, that allows us to override existing types
+		*/
+		if (isset($this->connection->customMetaTypes[$meta]))
+			return $this->connection->customMetaTypes[$meta]['actual'];
+		
 		switch($meta) {
 		case 'C': return 'VARCHAR';// 255
 		case 'XL':
diff --git a/lib/adodb/datadict/datadict-mssql.inc.php b/lib/adodb/datadict/datadict-mssql.inc.php
index 1bcb27da169..17df9e39675 100644
--- a/lib/adodb/datadict/datadict-mssql.inc.php
+++ b/lib/adodb/datadict/datadict-mssql.inc.php
@@ -68,9 +68,14 @@ class ADODB2_mssql extends ADODB_DataDict {
 			$t = $fieldobj->type;
 			$len = $fieldobj->max_length;
 		}
+		
+		$t = strtoupper($t);
+		
+		if (array_key_exists($t,$this->connection->customActualTypes))
+			return  $this->connection->customActualTypes[$t];
 
 		$len = -1; // mysql max_length is not accurate
-		switch (strtoupper($t)) {
+		switch ($t) {
 		case 'R':
 		case 'INT':
 		case 'INTEGER': return  'I';
@@ -87,6 +92,16 @@ class ADODB2_mssql extends ADODB_DataDict {
 
 	function ActualType($meta)
 	{
+		
+		$meta = strtoupper($meta);
+		
+		/*
+		* Add support for custom meta types. We do this
+		* first, that allows us to override existing types
+		*/
+		if (isset($this->connection->customMetaTypes[$meta]))
+			return $this->connection->customMetaTypes[$meta]['actual'];
+		
 		switch(strtoupper($meta)) {
 
 		case 'C': return 'VARCHAR';
diff --git a/lib/adodb/datadict/datadict-mssqlnative.inc.php b/lib/adodb/datadict/datadict-mssqlnative.inc.php
index b53dcd97b0f..59228cbe393 100644
--- a/lib/adodb/datadict/datadict-mssqlnative.inc.php
+++ b/lib/adodb/datadict/datadict-mssqlnative.inc.php
@@ -71,7 +71,13 @@ class ADODB2_mssqlnative extends ADODB_DataDict {
 			$fieldobj = $t;
 			$t = $fieldobj->type;
 		}
-
+		
+	
+		$t = strtoupper($t);
+		
+		if (array_key_exists($t,$this->connection->customActualTypes))
+			return  $this->connection->customActualTypes[$t];
+		
 		$_typeConversion = array(
 			-155 => 'D',
 			  93 => 'D',
@@ -115,7 +121,15 @@ class ADODB2_mssqlnative extends ADODB_DataDict {
 	function ActualType($meta)
 	{
 		$DATE_TYPE = 'DATETIME';
-
+		$meta = strtoupper($meta);
+		
+		/*
+		* Add support for custom meta types. We do this
+		* first, that allows us to override existing types
+		*/
+		if (isset($this->connection->customMetaTypes[$meta]))
+			return $this->connection->customMetaTypes[$meta]['actual'];
+		
 		switch(strtoupper($meta)) {
 
 		case 'C': return 'VARCHAR';
diff --git a/lib/adodb/datadict/datadict-mysql.inc.php b/lib/adodb/datadict/datadict-mysql.inc.php
index a1ee950a6ba..9efbba1f316 100644
--- a/lib/adodb/datadict/datadict-mysql.inc.php
+++ b/lib/adodb/datadict/datadict-mysql.inc.php
@@ -33,7 +33,7 @@ class ADODB2_mysql extends ADODB_DataDict {
 
 	public $blobAllowsNotNull = true;
 	
-	function MetaType($t,$len=-1,$fieldobj=false)
+	function metaType($t,$len=-1,$fieldobj=false)
 	{
 		
 		if (is_object($t)) {
@@ -44,7 +44,14 @@ class ADODB2_mysql extends ADODB_DataDict {
 		$is_serial = is_object($fieldobj) && $fieldobj->primary_key && $fieldobj->auto_increment;
 
 		$len = -1; // mysql max_length is not accurate
-		switch (strtoupper($t)) {
+			
+		$t = strtoupper($t);
+		
+		if (array_key_exists($t,$this->connection->customActualTypes))
+			return  $this->connection->customActualTypes[$t];
+		
+		switch ($t) {
+			
 		case 'STRING':
 		case 'CHAR':
 		case 'VARCHAR':
@@ -84,13 +91,27 @@ class ADODB2_mysql extends ADODB_DataDict {
 		case 'SMALLINT': return $is_serial ? 'R' : 'I2';
 		case 'MEDIUMINT': return $is_serial ? 'R' : 'I4';
 		case 'BIGINT':  return $is_serial ? 'R' : 'I8';
-		default: return ADODB_DEFAULT_METATYPE;
+		default: 
+			
+			return ADODB_DEFAULT_METATYPE;
 		}
 	}
 
 	function ActualType($meta)
 	{
-		switch(strtoupper($meta)) {
+		
+		$meta = strtoupper($meta);
+		
+		/*
+		* Add support for custom meta types. We do this
+		* first, that allows us to override existing types
+		*/
+		if (isset($this->connection->customMetaTypes[$meta]))
+			return $this->connection->customMetaTypes[$meta]['actual'];
+				
+		switch($meta) 
+		{
+		
 		case 'C': return 'VARCHAR';
 		case 'XL':return 'LONGTEXT';
 		case 'X': return 'TEXT';
@@ -114,7 +135,9 @@ class ADODB2_mysql extends ADODB_DataDict {
 
 		case 'F': return 'DOUBLE';
 		case 'N': return 'NUMERIC';
+			
 		default:
+			
 			return $meta;
 		}
 	}
diff --git a/lib/adodb/datadict/datadict-oci8.inc.php b/lib/adodb/datadict/datadict-oci8.inc.php
index 9a239095cde..6d2cd244f5c 100644
--- a/lib/adodb/datadict/datadict-oci8.inc.php
+++ b/lib/adodb/datadict/datadict-oci8.inc.php
@@ -50,7 +50,13 @@ class ADODB2_oci8 extends ADODB_DataDict {
 			$t = $fieldobj->type;
 			$len = $fieldobj->max_length;
 		}
-		switch (strtoupper($t)) {
+		
+		$t = strtoupper($t);
+		
+		if (array_key_exists($t,$this->connection->customActualTypes))
+			return  $this->connection->customActualTypes[$t];
+
+		switch ($t) {
 	 	case 'VARCHAR':
 	 	case 'VARCHAR2':
 		case 'CHAR':
@@ -92,6 +98,15 @@ class ADODB2_oci8 extends ADODB_DataDict {
 
  	function ActualType($meta)
 	{
+		$meta = strtoupper($meta);
+		
+		/*
+		* Add support for custom meta types. We do this
+		* first, that allows us to override existing types
+		*/
+		if (isset($this->connection->customMetaTypes[$meta]))
+			return $this->connection->customMetaTypes[$meta]['actual'];
+		
 		switch($meta) {
 		case 'C': return 'VARCHAR';
 		case 'X': return $this->typeX;
diff --git a/lib/adodb/datadict/datadict-postgres.inc.php b/lib/adodb/datadict/datadict-postgres.inc.php
index 17627c44c18..d403cee3056 100644
--- a/lib/adodb/datadict/datadict-postgres.inc.php
+++ b/lib/adodb/datadict/datadict-postgres.inc.php
@@ -34,7 +34,7 @@ class ADODB2_postgres extends ADODB_DataDict
 
 	public $blobAllowsDefaultValue = true;
 	public $blobAllowsNotNull = true;
-	
+
 	function metaType($t, $len=-1, $fieldobj=false)
 	{
 		if (is_object($t)) {
@@ -42,16 +42,23 @@ class ADODB2_postgres extends ADODB_DataDict
 			$t = $fieldobj->type;
 			$len = $fieldobj->max_length;
 		}
+
+		$t = strtoupper($t);
+
+		if (array_key_exists($t,$this->connection->customActualTypes))
+			return  $this->connection->customActualTypes[$t];
+
 		$is_serial = is_object($fieldobj) && !empty($fieldobj->primary_key) && !empty($fieldobj->unique) &&
 			!empty($fieldobj->has_default) && substr($fieldobj->default_value,0,8) == 'nextval(';
 
-		switch (strtoupper($t)) {
+		switch ($t) {
+
 			case 'INTERVAL':
 			case 'CHAR':
 			case 'CHARACTER':
 			case 'VARCHAR':
 			case 'NAME':
-	   		case 'BPCHAR':
+			case 'BPCHAR':
 				if ($len <= $this->blobSize) return 'C';
 
 			case 'TEXT':
@@ -94,13 +101,22 @@ class ADODB2_postgres extends ADODB_DataDict
 			case 'REAL':
 				return 'F';
 
-			 default:
-			 	return ADODB_DEFAULT_METATYPE;
+			default:
+				return ADODB_DEFAULT_METATYPE;
 		}
 	}
 
- 	function actualType($meta)
+	function actualType($meta)
 	{
+		$meta = strtoupper($meta);
+
+		/*
+		* Add support for custom meta types. We do this
+		* first, that allows us to override existing types
+		*/
+		if (isset($this->connection->customMetaTypes[$meta]))
+			return $this->connection->customMetaTypes[$meta]['actual'];
+
 		switch ($meta) {
 		case 'C': return 'VARCHAR';
 		case 'XL':
@@ -152,7 +168,7 @@ class ADODB2_postgres extends ADODB_DataDict
 			if (preg_match('/^([^ ]+) .*DEFAULT (\'[^\']+\'|\"[^\"]+\"|[^ ]+)/',$v,$matches)) {
 				list(,$colname,$default) = $matches;
 				$sql[] = $alter . str_replace('DEFAULT '.$default,'',$v);
-				$sql[] = 'UPDATE '.$tabname.' SET '.$colname.'='.$default;
+				$sql[] = 'UPDATE '.$tabname.' SET '.$colname.'='.$default.' WHERE '.$colname.' IS NULL ';
 				$sql[] = 'ALTER TABLE '.$tabname.' ALTER COLUMN '.$colname.' SET DEFAULT ' . $default;
 			} else {
 				$sql[] = $alter . $v;
@@ -168,18 +184,21 @@ class ADODB2_postgres extends ADODB_DataDict
 
 	function dropIndexSQL($idxname, $tabname = NULL)
 	{
-	   return array(sprintf($this->dropIndex, $this->tableName($idxname), $this->tableName($tabname)));
+		return array(sprintf($this->dropIndex, $this->tableName($idxname), $this->tableName($tabname)));
 	}
 
 	/**
 	 * Change the definition of one column
 	 *
-	 * Postgres can't do that on it's own, you need to supply the complete definition of the new table,
-	 * to allow, recreating the table and copying the content over to the new table
-	 * @param string $tabname table-name
-	 * @param string $flds column-name and type for the changed column
-	 * @param string $tableflds complete definition of the new table, eg. for postgres, default ''
-	 * @param array/ $tableoptions options for the new table see CreateTableSQL, default ''
+	 * Postgres can't do that on its own, you need to supply the complete
+	 * definition of the new table, to allow recreating the table and copying
+	 * the content over to the new table.
+	 *
+	 * @param string $tabname      table-name
+	 * @param string $flds         column-name and type for the changed column
+	 * @param string $tableflds    complete definition of the new table, e.g. for postgres, default ''
+	 * @param array  $tableoptions options for the new table {@see CreateTableSQL()}, default ''
+	 *
 	 * @return array with SQL strings
 	 */
 	function alterColumnSQL($tabname, $flds, $tableflds='', $tableoptions='')
@@ -197,9 +216,9 @@ class ADODB2_postgres extends ADODB_DataDict
 				if ($not_null = preg_match('/NOT NULL/i',$v)) {
 					$v = preg_replace('/NOT NULL/i','',$v);
 				}
-				 // this next block doesn't work - there is no way that I can see to
-				 // explicitly ask a column to be null using $flds
-				else if ($set_null = preg_match('/NULL/i',$v)) {
+				// this next block doesn't work - there is no way that I can see to
+				// explicitly ask a column to be null using $flds
+				elseif ($set_null = preg_match('/NULL/i',$v)) {
 					// if they didn't specify not null, see if they explicitly asked for null
 					// Lookbehind pattern covers the case 'fieldname NULL datatype DEFAULT NULL'
 					// only the first NULL should be removed, not the one specifying
@@ -278,7 +297,7 @@ class ADODB2_postgres extends ADODB_DataDict
 	 * @param string $tabname table-name
 	 * @param string $flds column-name and type for the changed column
 	 * @param string $tableflds complete definition of the new table, eg. for postgres, default ''
-	 * @param array/ $tableoptions options for the new table see CreateTableSQL, default ''
+	 * @param array  $tableoptions options for the new table {@see CreateTableSQL}, default []
 	 * @return array with SQL strings
 	 */
 	function dropColumnSQL($tabname, $flds, $tableflds='', $tableoptions='')
@@ -305,7 +324,7 @@ class ADODB2_postgres extends ADODB_DataDict
 	 * @param string $tabname table-name
 	 * @param string $dropflds column-names to drop
 	 * @param string $tableflds complete definition of the new table, eg. for postgres
-	 * @param array/string $tableoptions options for the new table see CreateTableSQL, default ''
+	 * @param array|string $tableoptions options for the new table see CreateTableSQL, default ''
 	 * @return array with SQL strings
 	 */
 	function _recreate_copy_table($tabname, $dropflds, $tableflds, $tableoptions='')
@@ -477,11 +496,11 @@ CREATE [ UNIQUE ] INDEX index_name ON table
 		if (isset($idxoptions['HASH'])) {
 			$s .= 'USING HASH ';
 		}
-		
+
 		if (isset($idxoptions[$this->upperName])) {
 			$s .= $idxoptions[$this->upperName];
 		}
-		
+
 		if (is_array($flds)) {
 			$flds = implode(', ', $flds);
 		}
@@ -514,7 +533,7 @@ CREATE [ UNIQUE ] INDEX index_name ON table
 		}
 		return $ftype;
 	}
-	
+
 	function changeTableSQL($tablename, $flds, $tableoptions = false, $dropOldFlds=false)
 	{
 		global $ADODB_FETCH_MODE;
@@ -524,18 +543,18 @@ CREATE [ UNIQUE ] INDEX index_name ON table
 		if ($this->connection->fetchMode !== false) {
 			$savem = $this->connection->setFetchMode(false);
 		}
-		
+
 		// check table exists
 		$save_handler = $this->connection->raiseErrorFn;
 		$this->connection->raiseErrorFn = '';
 		$cols = $this->metaColumns($tablename);
 		$this->connection->raiseErrorFn = $save_handler;
-		
+
 		if (isset($savem)) {
 			$this->connection->setFetchMode($savem);
 		}
 		$ADODB_FETCH_MODE = $save;
-		
+
 		$sqlResult=array();
 		if ( empty($cols)) {
 			$sqlResult=$this->createTableSQL($tablename, $flds, $tableoptions);
@@ -543,7 +562,7 @@ CREATE [ UNIQUE ] INDEX index_name ON table
 			$sqlResultAdd = $this->addColumnSQL($tablename, $flds);
 			$sqlResultAlter = $this->alterColumnSQL($tablename, $flds, '', $tableoptions);
 			$sqlResult = array_merge((array)$sqlResultAdd, (array)$sqlResultAlter);
-			
+
 			if ($dropOldFlds) {
 				// already exists, alter table instead
 				list($lines,$pkey,$idxs) = $this->_genFields($flds);
@@ -558,7 +577,7 @@ CREATE [ UNIQUE ] INDEX index_name ON table
 					}
 				}
 			}
-			
+
 		}
 		return $sqlResult;
 	}
diff --git a/lib/adodb/datadict/datadict-sapdb.inc.php b/lib/adodb/datadict/datadict-sapdb.inc.php
index 20c16aa6c50..c469800f2ef 100644
--- a/lib/adodb/datadict/datadict-sapdb.inc.php
+++ b/lib/adodb/datadict/datadict-sapdb.inc.php
@@ -30,6 +30,15 @@ class ADODB2_sapdb extends ADODB_DataDict {
 
  	function ActualType($meta)
 	{
+		$meta = strtoupper($meta);
+		
+		/*
+		* Add support for custom meta types. We do this
+		* first, that allows us to override existing types
+		*/
+		if (isset($this->connection->customMetaTypes[$meta]))
+			return $this->connection->customMetaTypes[$meta]['actual'];
+		
 		switch($meta) {
 		case 'C': return 'VARCHAR';
 		case 'XL':
@@ -65,6 +74,12 @@ class ADODB2_sapdb extends ADODB_DataDict {
 			$t = $fieldobj->type;
 			$len = $fieldobj->max_length;
 		}
+		
+		$t = strtoupper($t);
+		
+		if (array_key_exists($t,$this->connection->customActualTypes))
+			return  $this->connection->customActualTypes[$t];
+
 		static $maxdb_type2adodb = array(
 			'VARCHAR'	=> 'C',
 			'CHARACTER'	=> 'C',
diff --git a/lib/adodb/datadict/datadict-sqlite.inc.php b/lib/adodb/datadict/datadict-sqlite.inc.php
index 942927f8a61..d565f887752 100644
--- a/lib/adodb/datadict/datadict-sqlite.inc.php
+++ b/lib/adodb/datadict/datadict-sqlite.inc.php
@@ -35,6 +35,16 @@ class ADODB2_sqlite extends ADODB_DataDict {
     
 	function ActualType($meta)
 	{
+		
+		$meta = strtoupper($meta);
+		
+		/*
+		* Add support for custom meta types. We do this
+		* first, that allows us to override existing types
+		*/
+		if (isset($this->connection->customMetaTypes[$meta]))
+			return $this->connection->customMetaTypes[$meta]['actual'];
+		
 		switch(strtoupper($meta)) {
 		case 'C': return 'VARCHAR'; //  TEXT , TEXT affinity
 		case 'XL':return 'LONGTEXT'; //  TEXT , TEXT affinity
diff --git a/lib/adodb/datadict/datadict-sybase.inc.php b/lib/adodb/datadict/datadict-sybase.inc.php
index e565f8e21a4..d6573dfa3f7 100644
--- a/lib/adodb/datadict/datadict-sybase.inc.php
+++ b/lib/adodb/datadict/datadict-sybase.inc.php
@@ -35,8 +35,15 @@ class ADODB2_sybase extends ADODB_DataDict {
 			$len = $fieldobj->max_length;
 		}
 
+		$t = strtoupper($t);
+		
+		if (array_key_exists($t,$this->connection->customActualTypes))
+			return  $this->connection->customActualTypes[$t];
+
 		$len = -1; // mysql max_length is not accurate
-		switch (strtoupper($t)) {
+
+		switch ($t) {
+
 
 		case 'INT':
 		case 'INTEGER': return  'I';
@@ -53,6 +60,15 @@ class ADODB2_sybase extends ADODB_DataDict {
 
 	function ActualType($meta)
 	{
+		$meta = strtoupper($meta);
+		
+		/*
+		* Add support for custom meta types. We do this
+		* first, that allows us to override existing types
+		*/
+		if (isset($this->connection->customMetaTypes[$meta]))
+			return $this->connection->customMetaTypes[$meta]['actual'];
+		
 		switch(strtoupper($meta)) {
 		case 'C': return 'VARCHAR';
 		case 'XL':
diff --git a/lib/adodb/drivers/adodb-ado.inc.php b/lib/adodb/drivers/adodb-ado.inc.php
index 67a032d7220..fc000cecb38 100644
--- a/lib/adodb/drivers/adodb-ado.inc.php
+++ b/lib/adodb/drivers/adodb-ado.inc.php
@@ -504,6 +504,9 @@ class ADORecordSet_ado extends ADORecordSet {
 			$t = $fieldobj->type;
 			$len = $fieldobj->max_length;
 		}
+		
+		if (array_key_exists($t,$this->connection->customActualTypes))
+			return  $this->connection->customActualTypes[$t];
 
 		if (!is_numeric($t)) return $t;
 
diff --git a/lib/adodb/drivers/adodb-ado5.inc.php b/lib/adodb/drivers/adodb-ado5.inc.php
index f673d09298d..36f9c3b8d5b 100644
--- a/lib/adodb/drivers/adodb-ado5.inc.php
+++ b/lib/adodb/drivers/adodb-ado5.inc.php
@@ -545,8 +545,14 @@ class ADORecordSet_ado extends ADORecordSet {
 			$t = $fieldobj->type;
 			$len = $fieldobj->max_length;
 		}
+		
+		$t = strtoupper($t);
+		
+		if (array_key_exists($t,$this->connection->customActualTypes))
+			return  $this->connection->customActualTypes[$t];
 
-		if (!is_numeric($t)) return $t;
+		if (!is_numeric($t)) 
+			return $t;
 
 		switch ($t) {
 		case 0:
diff --git a/lib/adodb/drivers/adodb-db2.inc.php b/lib/adodb/drivers/adodb-db2.inc.php
index 6d1cf16c5c4..16c4054f174 100644
--- a/lib/adodb/drivers/adodb-db2.inc.php
+++ b/lib/adodb/drivers/adodb-db2.inc.php
@@ -113,7 +113,7 @@ class ADODB_db2 extends ADOConnection {
 
 	private function doDB2Connect($argDSN, $argUsername, $argPassword, $argDatabasename, $persistent=false)
 	{
-
+		
 		if (!function_exists('db2_connect')) {
 			ADOConnection::outp("DB2 extension not installed.");
 			return null;
@@ -184,6 +184,7 @@ class ADODB_db2 extends ADOConnection {
 												null,
 												$db2Options);
 
+		
 		$this->_errorMsg = @db2_conn_errormsg();
 
 		if ($this->_connectionID && $this->connectStmt)
@@ -206,6 +207,7 @@ class ADODB_db2 extends ADOConnection {
 	private function unpackParameters($argDSN, $argUsername, $argPassword, $argDatabasename)
 	{
 
+		
 		$connectionParameters = array('dsn'=>'',
 									  'uid'=>'',
 									  'pwd'=>'',
@@ -255,6 +257,7 @@ class ADODB_db2 extends ADOConnection {
 				$errorMessage = 'Supply uncatalogued connection parameters ';
 				$errorMessage.= 'in either the database or DSN arguments, ';
 				$errorMessage.= 'but not both';
+			
 				if ($this->debug)
 					ADOConnection::outp($errorMessage);
 				return null;
@@ -279,6 +282,7 @@ class ADODB_db2 extends ADOConnection {
 			{
 				$errorMessage = 'For uncatalogued connections, provide ';
 				$errorMessage.= 'both UID and PWD in the connection string';
+				
 				if ($this->debug)
 					ADOConnection::outp($errorMessage);
 				return null;
@@ -313,6 +317,7 @@ class ADODB_db2 extends ADOConnection {
 			{
 				$errorMessage = 'Uncatalogued connection parameters ';
 				$errorMessage.= 'must contain a database= argument';
+				
 				if ($this->debug)
 					ADOConnection::outp($errorMessage);
 				return null;
@@ -676,16 +681,17 @@ class ADODB_db2 extends ADOConnection {
 	}
 
 	/**
-	 * returns assoc array where keys are tables, and values are foreign keys
+	 * Returns a list of Foreign Keys associated with a specific table.
 	 *
-	 * @param	string	$table
-	 * @param	string	$owner		[optional][discarded]
-	 * @param	bool	$upper		[optional][discarded]
-	 * @param	bool	$associative[optional][discarded]
+	 * @param string $table
+	 * @param string $owner       discarded
+	 * @param bool   $upper       discarded
+	 * @param bool   $associative discarded
 	 *
-	 * @return	mixed[]			Array of foreign key information
+	 * @return string[]|false An array where keys are tables, and values are foreign keys;
+	 *                        false if no foreign keys could be found.
 	 */
-	public function metaForeignKeys($table, $owner = FALSE, $upper = FALSE, $asociative = FALSE )
+	public function metaForeignKeys($table, $owner = '', $upper = false, $associative = false)
 	{
 
 		global $ADODB_FETCH_MODE;
@@ -1610,12 +1616,12 @@ See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/db2/htm/db2
 
 				if ($stmtid == false)
 				{
-                    $this->_errorMsg  = @db2_stmt_errormsg();
-                    $this->_errorCode = @db2_stmt_error();
-
-                    if ($this->debug)
-                        ADOConnection::outp($this->_errorMsg);
-
+					$this->_errorMsg  = @db2_stmt_errormsg();
+					$this->_errorCode = @db2_stmt_error();
+					
+					if ($this->debug)
+						ADOConnection::outp($this->_errorMsg);
+					
 					return false;
 				}
 			}
diff --git a/lib/adodb/drivers/adodb-fbsql.inc.php b/lib/adodb/drivers/adodb-fbsql.inc.php
index 0fb895a1d05..a4255eb97ed 100644
--- a/lib/adodb/drivers/adodb-fbsql.inc.php
+++ b/lib/adodb/drivers/adodb-fbsql.inc.php
@@ -232,8 +232,15 @@ class ADORecordSet_fbsql extends ADORecordSet{
 			$t = $fieldobj->type;
 			$len = $fieldobj->max_length;
 		}
+
+		$t = strtoupper($t);
+
+		if (array_key_exists($t,$this->connection->customActualTypes))
+			return $this->connection->customActualTypes[$t];
+
 		$len = -1; // fbsql max_length is not accurate
-		switch (strtoupper($t)) {
+
+		switch ($t) {
 		case 'CHARACTER':
 		case 'CHARACTER VARYING':
 		case 'BLOB':
diff --git a/lib/adodb/drivers/adodb-firebird.inc.php b/lib/adodb/drivers/adodb-firebird.inc.php
index 2fafbe4fcee..135b72c6423 100644
--- a/lib/adodb/drivers/adodb-firebird.inc.php
+++ b/lib/adodb/drivers/adodb-firebird.inc.php
@@ -19,6 +19,10 @@
  *
  * @copyright 2000-2013 John Lim
  * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community
+ *
+ * Driver was cloned from Interbase, so there's quite a lot of duplicated code
+ * @noinspection DuplicatedCode
+ * @noinspection PhpUnused
  */
 
 // security - hide paths
@@ -35,39 +39,113 @@ class ADODB_firebird extends ADOConnection {
 	var $fmtTimeStamp = "'Y-m-d, H:i:s'";
 	var $concat_operator='||';
 	var $_transactionID;
-	var $metaTablesSQL = "select lower(rdb\$relation_name) from rdb\$relations where rdb\$relation_name not like 'RDB\$%'";
+
+	public $metaTablesSQL = "SELECT LOWER(rdb\$relation_name) FROM rdb\$relations";
 	//OPN STUFF start
+
 	var $metaColumnsSQL = "select lower(a.rdb\$field_name), a.rdb\$null_flag, a.rdb\$default_source, b.rdb\$field_length, b.rdb\$field_scale, b.rdb\$field_sub_type, b.rdb\$field_precision, b.rdb\$field_type from rdb\$relation_fields a, rdb\$fields b where a.rdb\$field_source = b.rdb\$field_name and a.rdb\$relation_name = '%s' order by a.rdb\$field_position asc";
 	//OPN STUFF end
-	var $ibasetrans;
+
+	public $_genSeqSQL = "CREATE SEQUENCE %s START WITH %s";
+
+	public $_dropSeqSQL = "DROP SEQUENCE %s";
+
 	var $hasGenID = true;
 	var $_bindInputArray = true;
-	var $buffers = 0;
-	var $dialect = 3;
 	var $sysDate = "cast('TODAY' as timestamp)";
 	var $sysTimeStamp = "CURRENT_TIMESTAMP"; //"cast('NOW' as timestamp)";
 	var $ansiOuter = true;
 	var $hasAffectedRows = true;
 	var $poorAffectedRows = false;
 	var $blobEncodeType = 'C';
-	var $role = false;
+	/*
+	* firebird custom optionally specifies the user role
+	*/
+	public $role = false;
+	/*
+	* firebird custom optionally specifies the connection buffers
+	*/
+	public $buffers = 0;
+
+	/*
+	* firebird custom optionally specifies database dialect
+	*/
+	public $dialect = 3;
+
 	var $nameQuote = '';		/// string to use to quote identifiers and names
 
 	function __construct()
 	{
-	// Ignore IBASE_DEFAULT we want a more practical transaction!
-	//	if (defined('IBASE_DEFAULT')) $this->ibasetrans = IBASE_DEFAULT;
-	//	else
-		$this->ibasetrans = IBASE_WAIT | IBASE_REC_VERSION | IBASE_COMMITTED;
+		parent::__construct();
+		$this->setTransactionMode('');
 	}
 
-
-	// returns true or false
-	function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$persist=false)
+	/**
+	 * Sets the isolation level of a transaction.
+	 *
+	 * The default behavior is a more practical IBASE_WAIT | IBASE_REC_VERSION | IBASE_COMMITTED
+	 * instead of IBASE_DEFAULT
+	 *
+	 * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:settransactionmode
+	 *
+	 * @param string $transaction_mode The transaction mode to set.
+	 *
+	 * @return void
+	 */
+	public function setTransactionMode($transaction_mode)
 	{
-		if (!function_exists('fbird_pconnect')) return null;
-		if ($argDatabasename) $argHostname .= ':'.$argDatabasename;
+		$this->_transmode = $transaction_mode;
+
+		if (empty($transaction_mode)) {
+			$this->_transmode = IBASE_WAIT | IBASE_REC_VERSION | IBASE_COMMITTED;
+		}
+
+	}
+
+	/**
+	 * Connect to a database.
+	 *
+	 * @todo add: parameter int $port, parameter string $socket
+	 *
+	 * @param string|null $argHostname (Optional) The host to connect to.
+	 * @param string|null $argUsername (Optional) The username to connect as.
+	 * @param string|null $argPassword (Optional) The password to connect with.
+	 * @param string|null $argDatabasename (Optional) The name of the database to start in when connected.
+	 * @param bool $persist (Optional) Whether or not to use a persistent connection.
+	 *
+	 * @return bool|null True if connected successfully, false if connection failed, or null if the mysqli extension
+	 * isn't currently loaded.
+	 */
+	public function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$persist=false)
+	{
+		if (!function_exists('fbird_pconnect'))
+			return null;
+
+		if ($argDatabasename)
+			$argHostname .= ':'.$argDatabasename;
+
 		$fn = ($persist) ? 'fbird_pconnect':'fbird_connect';
+
+		/*
+		* Now merge in the standard connection parameters setting
+		*/
+		foreach ($this->connectionParameters as $options)
+		{
+			foreach($options as $k=>$v)
+			{
+				switch($k){
+				case 'role':
+					$this->role = $v;
+					break;
+				case 'dialect':
+					$this->dialect = $v;
+					break;
+				case 'buffers':
+					$this->buffers = $v;
+				}
+			}
+		}
+
 		if ($this->role)
 			$this->_connectionID = $fn($argHostname,$argUsername,$argPassword,
 					$this->charSet,$this->buffers,$this->dialect,$this->role);
@@ -75,40 +153,39 @@ class ADODB_firebird extends ADOConnection {
 			$this->_connectionID = $fn($argHostname,$argUsername,$argPassword,
 					$this->charSet,$this->buffers,$this->dialect);
 
-		if ($this->dialect != 1) { // http://www.ibphoenix.com/ibp_60_del_id_ds.html
-			$this->replaceQuote = "''";
+		if ($this->dialect == 1) { // http://www.ibphoenix.com/ibp_60_del_id_ds.html
+			$this->replaceQuote = "";
 		}
 		if ($this->_connectionID === false) {
-			$this->_handleerror();
+			$this->_handleError();
 			return false;
 		}
 
-		// PHP5 change.
-		if (function_exists('fbird_timefmt')) {
-			fbird_timefmt($this->fbird_datefmt,fbird_DATE );
-			if ($this->dialect == 1) {
-				fbird_timefmt($this->fbird_datefmt,fbird_TIMESTAMP );
-			} else {
-				fbird_timefmt($this->fbird_timestampfmt,fbird_TIMESTAMP );
-			}
-			fbird_timefmt($this->fbird_timefmt,fbird_TIME );
+		ini_set("ibase.timestampformat", $this->fbird_timestampfmt);
+		ini_set("ibase.dateformat", $this->fbird_datefmt);
+		ini_set("ibase.timeformat", $this->fbird_timefmt);
 
-		} else {
-			ini_set("ibase.timestampformat", $this->fbird_timestampfmt);
-			ini_set("ibase.dateformat", $this->fbird_datefmt);
-			ini_set("ibase.timeformat", $this->fbird_timefmt);
-		}
 		return true;
 	}
 
-	// returns true or false
+	/**
+	 * Connect to a database with a persistent connection.
+	 *
+	 * @param string|null $argHostname The host to connect to.
+	 * @param string|null $argUsername The username to connect as.
+	 * @param string|null $argPassword The password to connect with.
+	 * @param string|null $argDatabasename The name of the database to start in when connected.
+	 *
+	 * @return bool|null True if connected successfully, false if connection failed, or null if the mysqli extension
+	 * isn't currently loaded.
+	 */
 	function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
 	{
 		return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,true);
 	}
 
 
-	function MetaPrimaryKeys($table,$owner_notused=false,$internalKey=false)
+	public function metaPrimaryKeys($table,$owner_notused=false,$internalKey=false)
 	{
 		if ($internalKey) {
 			return array('RDB$DB_KEY');
@@ -126,31 +203,58 @@ class ADODB_firebird extends ADOConnection {
 		return false;
 	}
 
-	function ServerInfo()
+	/**
+	 * Get information about the current Firebird server.
+	 *
+	 * @return array
+	 */
+	public function serverInfo()
 	{
 		$arr['dialect'] = $this->dialect;
 		switch($arr['dialect']) {
-		case '':
-		case '1': $s = 'Firebird Dialect 1'; break;
-		case '2': $s = 'Firebird Dialect 2'; break;
-		default:
-		case '3': $s = 'Firebird Dialect 3'; break;
+			case '':
+			case '1':
+				$s = 'Firebird Dialect 1';
+				break;
+			case '2':
+				$s = 'Firebird Dialect 2';
+				break;
+			default:
+			case '3':
+				$s = 'Firebird Dialect 3';
+				break;
 		}
 		$arr['version'] = ADOConnection::_findvers($s);
 		$arr['description'] = $s;
 		return $arr;
 	}
 
-	function BeginTrans()
+	/**
+	 * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans().
+	 *
+	 * @return bool true if succeeded or false if database does not support transactions
+	 */
+	public function beginTrans()
 	{
 		if ($this->transOff) return true;
 		$this->transCnt += 1;
 		$this->autoCommit = false;
-		$this->_transactionID = fbird_trans( $this->ibasetrans, $this->_connectionID );
+		/*
+		* We manage the transaction mode via fbird_trans
+		*/
+		$this->_transactionID = fbird_trans( $this->_transmode, $this->_connectionID );
 		return $this->_transactionID;
 	}
 
-	function CommitTrans($ok=true)
+
+	/**
+	 * Commits a transaction.
+	 *
+	 * @param bool $ok  false to rollback transaction, true to commit
+	 *
+	 * @return bool
+	 */
+	public function commitTrans($ok=true)
 	{
 		if (!$ok) {
 			return $this->RollbackTrans();
@@ -164,7 +268,6 @@ class ADODB_firebird extends ADOConnection {
 		$ret = false;
 		$this->autoCommit = true;
 		if ($this->_transactionID) {
-			//print ' commit ';
 			$ret = fbird_commit($this->_transactionID);
 		}
 		$this->_transactionID = false;
@@ -173,31 +276,26 @@ class ADODB_firebird extends ADOConnection {
 
 	function _affectedrows()
 	{
-			return fbird_affected_rows( $this->_transactionID ? $this->_transactionID : $this->_connectionID );
+		return fbird_affected_rows($this->_transactionID ?: $this->_connectionID);
 	}
 
-	// there are some compat problems with ADODB_COUNTRECS=false and $this->_logsql currently.
-	// it appears that ibase extension cannot support multiple concurrent queryid's
-	function _Execute($sql,$inputarr=false) {
-	global $ADODB_COUNTRECS;
-
-		if ($this->_logsql) {
-			$savecrecs = $ADODB_COUNTRECS;
-			$ADODB_COUNTRECS = true; // force countrecs
-			$ret =& ADOConnection::_Execute($sql,$inputarr);
-			$ADODB_COUNTRECS = $savecrecs;
-		} else {
-			$ret = ADOConnection::_Execute($sql,$inputarr);
-		}
-		return $ret;
-	}
-
-	function RollbackTrans()
+	/**
+	 * Rollback a smart transaction.
+	 *
+	 * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:rollbacktrans
+	 *
+	 * @return bool
+	 */
+	public function rollbackTrans()
 	{
-		if ($this->transOff) return true;
-		if ($this->transCnt) $this->transCnt -= 1;
+		if ($this->transOff)
+			return true;
+		if ($this->transCnt)
+			$this->transCnt -= 1;
+
 		$ret = false;
 		$this->autoCommit = true;
+
 		if ($this->_transactionID) {
 			$ret = fbird_rollback($this->_transactionID);
 		}
@@ -206,16 +304,26 @@ class ADODB_firebird extends ADOConnection {
 		return $ret;
 	}
 
-	function &MetaIndexes ($table, $primary = FALSE, $owner=false)
+	/**
+	 * Get a list of indexes on the specified table.
+	 *
+	 * @param string $table The name of the table to get indexes for.
+	 * @param bool $primary (Optional) Whether or not to include the primary key.
+	 * @param bool $owner (Optional) Unused.
+	 *
+	 * @return array|bool An array of the indexes, or false if the query to get the indexes failed.
+	 */
+	public function metaIndexes($table, $primary = false, $owner = false)
 	{
 		// save old fetch mode
 		global $ADODB_FETCH_MODE;
-		$false = false;
 		$save = $ADODB_FETCH_MODE;
 		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+
 		if ($this->fetchMode !== FALSE) {
 				$savem = $this->SetFetchMode(FALSE);
 		}
+
 		$table = strtoupper($table);
 		$sql = "SELECT * FROM RDB\$INDICES WHERE RDB\$RELATION_NAME = '".$table."'";
 		if (!$primary) {
@@ -224,19 +332,19 @@ class ADODB_firebird extends ADOConnection {
 			$sql .= " AND RDB\$INDEX_NAME NOT LIKE 'RDB\$FOREIGN%'";
 		}
 		// get index details
-		$rs = $this->Execute($sql);
+		$rs = $this->execute($sql);
 		if (!is_object($rs)) {
 			// restore fetchmode
 			if (isset($savem)) {
 				$this->SetFetchMode($savem);
 			}
 			$ADODB_FETCH_MODE = $save;
-			return $false;
+			return false;
 		}
-
 		$indexes = array();
 		while ($row = $rs->FetchRow()) {
-			$index = $row[0];
+
+			$index = trim($row[0]);
 			if (!isset($indexes[$index])) {
 				if (is_null($row[3])) {
 					$row[3] = 0;
@@ -246,12 +354,13 @@ class ADODB_firebird extends ADOConnection {
 					'columns' => array()
 				);
 			}
-			$sql = "SELECT * FROM RDB\$INDEX_SEGMENTS WHERE RDB\$INDEX_NAME = '".$index."' ORDER BY RDB\$FIELD_POSITION ASC";
-			$rs1 = $this->Execute($sql);
+			$sql = sprintf("SELECT * FROM RDB\$INDEX_SEGMENTS WHERE RDB\$INDEX_NAME = '%s' ORDER BY RDB\$FIELD_POSITION ASC",$index);
+			$rs1 = $this->execute($sql);
 			while ($row1 = $rs1->FetchRow()) {
-				$indexes[$index]['columns'][$row1[2]] = $row1[1];
+				$indexes[$index]['columns'][$row1[2]] = trim($row1[1]);
 			}
 		}
+
 		// restore fetchmode
 		if (isset($savem)) {
 			$this->SetFetchMode($savem);
@@ -261,38 +370,60 @@ class ADODB_firebird extends ADOConnection {
 		return $indexes;
 	}
 
-
-	// See http://community.borland.com/article/0,1410,25844,00.html
-	function RowLock($tables,$where,$col=false)
+	/**
+	 * Lock a table row for a duration of a transaction.
+	 *
+	 * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:rowlock
+	 * @link https://firebirdsql.org/refdocs/langrefupd21-notes-withlock.html
+	 *
+	 * @param string $table The table(s) to lock rows for.
+	 * @param string $where (Optional) The WHERE clause to use to determine which rows to lock.
+	 * @param string $col (Optional) The columns to select.
+	 *
+	 * @return bool True if the locking SQL statement executed successfully, otherwise false.
+	 */
+	public function rowLock($table,$where,$col=false)
 	{
-		if ($this->autoCommit) {
-			$this->BeginTrans();
-		}
-		$this->Execute("UPDATE $table SET $col=$col WHERE $where "); // is this correct - jlim?
-		return 1;
+		if ($this->transCnt==0)
+			$this->beginTrans();
+
+		if ($where) $where = ' where '.$where;
+		$rs = $this->execute("SELECT $col FROM $table $where FOR UPDATE WITH LOCK");
+		return !empty($rs);
 	}
 
-
-	function CreateSequence($seqname = 'adodbseq', $startID = 1)
+	/**
+	 * Creates a sequence in the database.
+	 *
+	 * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:createsequence
+	 *
+	 * @param string $seqname The sequence name.
+	 * @param int $startID The start id.
+	 *
+	 * @return ADORecordSet|bool A record set if executed successfully, otherwise false.
+	 */
+	public function createSequence($seqname='adodbseq', $startID = 1)
 	{
-		$ok = $this->Execute(("CREATE GENERATOR $seqname" ));
-		if (!$ok) return false;
-		return $this->Execute("SET GENERATOR $seqname TO ".($startID-1));
+		$sql = sprintf($this->_genSeqSQL,$seqname,$startID);
+		return $this->execute($sql);
 	}
 
-	function DropSequence($seqname = 'adodbseq')
-	{
-		$seqname = strtoupper($seqname);
-		return $this->Execute("DROP GENERATOR $seqname");
-	}
-
-	function GenID($seqname='adodbseq',$startID=1)
+	/**
+	 * A portable method of creating sequence numbers.
+	 *
+	 * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:genid
+	 *
+	 * @param string $seqname (Optional) The name of the sequence to use.
+	 * @param int $startID (Optional) The point to start at in the sequence.
+	 *
+	 * @return int
+	 */
+	public function genID($seqname='adodbseq',$startID=1)
 	{
 		$getnext = ("SELECT Gen_ID($seqname,1) FROM RDB\$DATABASE");
 		$rs = @$this->Execute($getnext);
 		if (!$rs) {
-			$this->Execute(("CREATE GENERATOR $seqname" ));
-			$this->Execute("SET GENERATOR $seqname TO ".($startID-1).';');
+			$this->Execute("CREATE SEQUENCE $seqname START WITH $startID");
 			$rs = $this->Execute($getnext);
 		}
 		if ($rs && !$rs->EOF) {
@@ -309,39 +440,62 @@ class ADODB_firebird extends ADOConnection {
 		return $this->genID;
 	}
 
-	function SelectDB($dbName)
+	function selectDB($dbName)
 	{
 		return false;
 	}
 
-	function _handleerror()
+	function _handleError()
 	{
-		$this->_errorMsg = fbird_errmsg();
+		$this->_errorCode = fbird_errcode();
+		$this->_errorMsg  = fbird_errmsg();
 	}
 
-	function ErrorNo()
+
+	public function errorNo()
 	{
-		if (preg_match('/error code = ([\-0-9]*)/i', $this->_errorMsg,$arr)) return (integer) $arr[1];
-		else return 0;
+		return (integer) $this->_errorCode;
 	}
 
-	function ErrorMsg()
+	function errorMsg()
 	{
 			return $this->_errorMsg;
 	}
 
-	function Prepare($sql)
+	/**
+	 * Prepares an SQL statement and returns a handle to use.
+	 * This is not used by bound parameters anymore
+	 *
+	 * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:prepare
+	 * @todo update this function to handle prepared statements correctly
+	 *
+	 * @param string $sql The SQL to prepare.
+	 *
+	 * @return bool|array The SQL that was provided and the prepared parameters,
+	 *                    or false if the preparation fails
+	 */
+	public function prepare($sql)
 	{
 		$stmt = fbird_prepare($this->_connectionID,$sql);
-		if (!$stmt) return false;
+		if (!$stmt)
+			return false;
 		return array($sql,$stmt);
 	}
 
-	   // returns query ID if successful, otherwise false
-	   // there have been reports of problems with nested queries - the code is probably not re-entrant?
-	function _query($sql,$iarr=false)
+	/**
+	* Return the query id.
+	*
+	* @param string|array $sql
+	* @param array $iarr
+	*
+	* @return bool|object
+	*/
+	function _query($sql, $iarr = false)
 	{
-		if ( !$this->isConnected() ) return false;
+		if (!$this->isConnected()) {
+			return false;
+		}
+
 		if (!$this->autoCommit && $this->_transactionID) {
 			$conn = $this->_transactionID;
 			$docommit = false;
@@ -349,36 +503,25 @@ class ADODB_firebird extends ADOConnection {
 			$conn = $this->_connectionID;
 			$docommit = true;
 		}
+
 		if (is_array($sql)) {
+			// Prepared statement
 			$fn = 'fbird_execute';
-			$sql = $sql[1];
-			if (is_array($iarr)) {
-				if ( !isset($iarr[0]) ) 
-					$iarr[0] = ''; // PHP5 compat hack
-				$fnarr = array_merge( array($sql) , $iarr);
-				$ret = call_user_func_array($fn,$fnarr);
-			}
-			else {
-				$ret = $fn($sql);
-			}
+			$args = [$sql[1]];
 		} else {
 			$fn = 'fbird_query';
-			if (is_array($iarr)) 
-			{
-				if (sizeof($iarr) == 0) 
-					$iarr[0] = ''; // PHP5 compat hack
-				$fnarr = array_merge( array($conn,$sql) , $iarr);
-				$ret = call_user_func_array($fn,$fnarr);
-			}
-			else {
-				$ret = $fn($conn, $sql);
-			}
+			$args = [$conn, $sql];
 		}
+		if (is_array($iarr)) {
+			$args = array_merge($args, $iarr);
+		}
+		$ret = call_user_func_array($fn, $args);
+
 		if ($docommit && $ret === true) {
 			fbird_commit($this->_connectionID);
 		}
 
-		$this->_handleerror();
+		$this->_handleError();
 		return $ret;
 	}
 
@@ -398,122 +541,133 @@ class ADODB_firebird extends ADOConnection {
 		$fld->max_length = $flen;
 		$fld->scale = null;
 		switch($ftype){
-			case 7:
-			case 8:
-				if ($dialect3) {
-					switch($fsubtype){
-						case 0:
-							$fld->type = ($ftype == 7 ? 'smallint' : 'integer');
-							break;
-						case 1:
-							$fld->type = 'numeric';
-							$fld->max_length = $fprecision;
-							$fld->scale = $fscale;
-							break;
-						case 2:
-							$fld->type = 'decimal';
-							$fld->max_length = $fprecision;
-							$fld->scale = $fscale;
-							break;
-					} // switch
-				} else {
-					if ($fscale !=0) {
-						$fld->type = 'decimal';
-						$fld->scale = $fscale;
-						$fld->max_length = ($ftype == 7 ? 4 : 9);
-					} else {
+		case 7:
+		case 8:
+			if ($dialect3) {
+				switch($fsubtype){
+					case 0:
 						$fld->type = ($ftype == 7 ? 'smallint' : 'integer');
-					}
-				}
-				break;
-			case 16:
-				if ($dialect3) {
-					switch($fsubtype){
-						case 0:
-							$fld->type = 'decimal';
-							$fld->max_length = 18;
-							$fld->scale = 0;
-							break;
-						case 1:
-							$fld->type = 'numeric';
-							$fld->max_length = $fprecision;
-							$fld->scale = $fscale;
-							break;
-						case 2:
-							$fld->type = 'decimal';
-							$fld->max_length = $fprecision;
-							$fld->scale = $fscale;
-							break;
-					} // switch
-				}
-				break;
-			case 10:
-				$fld->type = 'float';
-				break;
-			case 14:
-				$fld->type = 'char';
-				break;
-			case 27:
+						break;
+					case 1:
+						$fld->type = 'numeric';
+						$fld->max_length = $fprecision;
+						$fld->scale = $fscale;
+						break;
+					case 2:
+						$fld->type = 'decimal';
+						$fld->max_length = $fprecision;
+						$fld->scale = $fscale;
+						break;
+				} // switch
+			} else {
 				if ($fscale !=0) {
 					$fld->type = 'decimal';
-					$fld->max_length = 15;
-					$fld->scale = 5;
+					$fld->scale = $fscale;
+					$fld->max_length = ($ftype == 7 ? 4 : 9);
 				} else {
-					$fld->type = 'double';
+					$fld->type = ($ftype == 7 ? 'smallint' : 'integer');
 				}
-				break;
-			case 35:
-				if ($dialect3) {
-					$fld->type = 'timestamp';
-				} else {
-					$fld->type = 'date';
-				}
-				break;
-			case 12:
+			}
+			break;
+		case 16:
+			if ($dialect3) {
+				switch($fsubtype){
+				case 0:
+					$fld->type = 'decimal';
+					$fld->max_length = 18;
+					$fld->scale = 0;
+					break;
+				case 1:
+					$fld->type = 'numeric';
+					$fld->max_length = $fprecision;
+					$fld->scale = $fscale;
+					break;
+				case 2:
+					$fld->type = 'decimal';
+					$fld->max_length = $fprecision;
+					$fld->scale = $fscale;
+					break;
+				} // switch
+			}
+			break;
+		case 10:
+			$fld->type = 'float';
+			break;
+		case 14:
+			$fld->type = 'char';
+			break;
+		case 27:
+			if ($fscale !=0) {
+				$fld->type = 'decimal';
+				$fld->max_length = 15;
+				$fld->scale = 5;
+			} else {
+				$fld->type = 'double';
+			}
+			break;
+		case 35:
+			if ($dialect3) {
+				$fld->type = 'timestamp';
+			} else {
 				$fld->type = 'date';
-				break;
-			case 13:
-				$fld->type = 'time';
-				break;
-			case 37:
-				$fld->type = 'varchar';
-				break;
-			case 40:
-				$fld->type = 'cstring';
-				break;
-			case 261:
-				$fld->type = 'blob';
-				$fld->max_length = -1;
-				break;
+			}
+			break;
+		case 12:
+			$fld->type = 'date';
+			break;
+		case 13:
+			$fld->type = 'time';
+			break;
+		case 37:
+			$fld->type = 'varchar';
+			break;
+		case 40:
+			$fld->type = 'cstring';
+			break;
+		case 261:
+			$fld->type = 'blob';
+			$fld->max_length = -1;
+			break;
 		} // switch
 	}
 	//OPN STUFF end
 
-	// returns array of ADOFieldObjects for current table
-	function MetaColumns($table, $normalize=true)
+	/**
+	 * Return an array of information about a table's columns.
+	 *
+	 * @param string $table The name of the table to get the column info for.
+	 * @param bool $normalize (Optional) Unused.
+	 *
+	 * @return ADOFieldObject[]|bool An array of info for each column,
+	 * or false if it could not determine the info.
+	 */
+	public function metaColumns($table, $normalize = true)
 	{
-	global $ADODB_FETCH_MODE;
+
+		global $ADODB_FETCH_MODE;
 
 		$save = $ADODB_FETCH_MODE;
 		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 
-		$rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table)));
+		$rs = $this->execute(sprintf($this->metaColumnsSQL,strtoupper($table)));
 
 		$ADODB_FETCH_MODE = $save;
-		$false = false;
+
 		if ($rs === false) {
-			return $false;
+			return false;
 		}
 
 		$retarr = array();
 		//OPN STUFF start
-		$dialect3 = ($this->dialect==3 ? true : false);
+		$dialect3 = $this->dialect == 3;
 		//OPN STUFF end
 		while (!$rs->EOF) { //print_r($rs->fields);
 			$fld = new ADOFieldObject();
 			$fld->name = trim($rs->fields[0]);
 			//OPN STUFF start
-			$this->_ConvertFieldType($fld, $rs->fields[7], $rs->fields[3], $rs->fields[4], $rs->fields[5], $rs->fields[6], $dialect3);
+				//print_r($rs->fields);
+			$this->_ConvertFieldType(
+				$fld, $rs->fields[7], $rs->fields[3], $rs->fields[4], $rs->fields[5], $rs->fields[6], $dialect3);
 			if (isset($rs->fields[1]) && $rs->fields[1]) {
 				$fld->not_null = true;
 			}
@@ -521,17 +675,24 @@ class ADODB_firebird extends ADOConnection {
 
 				$fld->has_default = true;
 				$d = substr($rs->fields[2],strlen('default '));
-				switch ($fld->type)
-				{
-				case 'smallint':
-				case 'integer': $fld->default_value = (int) $d; break;
-				case 'char':
-				case 'blob':
-				case 'text':
-				case 'varchar': $fld->default_value = (string) substr($d,1,strlen($d)-2); break;
-				case 'double':
-				case 'float': $fld->default_value = (float) $d; break;
-				default: $fld->default_value = $d; break;
+				switch ($fld->type) {
+					case 'smallint':
+					case 'integer':
+						$fld->default_value = (int)$d;
+						break;
+					case 'char':
+					case 'blob':
+					case 'text':
+					case 'varchar':
+						$fld->default_value = (string)substr($d, 1, strlen($d) - 2);
+						break;
+					case 'double':
+					case 'float':
+						$fld->default_value = (float)$d;
+						break;
+					default:
+						$fld->default_value = $d;
+						break;
 				}
 		//	case 35:$tt = 'TIMESTAMP'; break;
 			}
@@ -547,61 +708,115 @@ class ADODB_firebird extends ADOConnection {
 			$rs->MoveNext();
 		}
 		$rs->Close();
-		if ( empty($retarr)) return $false;
+		if ( empty($retarr))
+			return false;
 		else return $retarr;
 	}
 
-	function BlobEncode( $blob )
+	/**
+	 * Retrieves a list of tables based on given criteria
+	 *
+	 * @param string|bool $ttype (Optional) Table type = 'TABLE', 'VIEW' or false=both (default)
+	 * @param string|bool $showSchema (Optional) schema name, false = current schema (default)
+	 * @param string|bool $mask (Optional) filters the table by name
+	 *
+	 * @return array list of tables
+	 */
+	public function metaTables($ttype = false, $showSchema = false, $mask = false)
+	{
+		$save = $this->metaTablesSQL;
+		if (!$showSchema) {
+			$this->metaTablesSQL .= " WHERE (rdb\$relation_name NOT LIKE 'RDB\$%' AND rdb\$relation_name NOT LIKE 'MON\$%' AND rdb\$relation_name NOT LIKE 'SEC\$%')";
+		} elseif (is_string($showSchema)) {
+			$this->metaTablesSQL .= $this->qstr($showSchema);
+		}
+
+		if ($mask) {
+			$mask = $this->qstr($mask);
+			$this->metaTablesSQL .= " AND table_name LIKE $mask";
+		}
+		$ret = ADOConnection::metaTables($ttype,$showSchema);
+
+		$this->metaTablesSQL = $save;
+		return $ret;
+	}
+
+	/**
+	 * Encodes a blob, then assigns an id ready to be used
+	 *
+	 * @param string $blob The blob to be encoded
+	 *
+	 * @return bool success
+	 */
+	public function blobEncode( $blob )
 	{
 		$blobid = fbird_blob_create( $this->_connectionID);
 		fbird_blob_add( $blobid, $blob );
 		return fbird_blob_close( $blobid );
 	}
 
-	// since we auto-decode all blob's since 2.42,
-	// BlobDecode should not do any transforms
-	function BlobDecode($blob)
+	/**
+	 * Manually decode a blob
+	 *
+	 * since we auto-decode all blob's since 2.42,
+	 * BlobDecode should not do any transforms
+	 *
+	 * @param string $blob
+	 *
+	 * @return string the same blob
+	 */
+	public function blobDecode($blob)
 	{
 		return $blob;
 	}
 
-	// old blobdecode function
-	// still used to auto-decode all blob's
-	function _BlobDecode_old( $blob )
+	/**
+	 * Auto function called on read of blob to decode
+	 *
+	 * @param string $blob Value to decode
+	 *
+	 * @return string Decoded blob
+	 */
+	public function _blobDecode($blob)
 	{
-		$blobid = fbird_blob_open($this->_connectionID, $blob );
-		$realblob = fbird_blob_get( $blobid,$this->maxblobsize); // 2nd param is max size of blob -- Kevin Boillet 
-		while($string = fbird_blob_get($blobid, 8192)){
-			$realblob .= $string;
+		if ($blob === null) {
+			return '';
 		}
-		fbird_blob_close( $blobid );
 
-		return( $realblob );
-	}
+		$blob_data = fbird_blob_info($this->_connectionID, $blob);
+		$blobId = fbird_blob_open($this->_connectionID, $blob);
 
-	function _BlobDecode( $blob )
-	{
-		$blob_data = fbird_blob_info($this->_connectionID, $blob );
-		$blobid    = fbird_blob_open($this->_connectionID, $blob );
-
-		if( $blob_data[0] > $this->maxblobsize ) {
-			$realblob = fbird_blob_get($blobid, $this->maxblobsize);
-
-			while($string = fbird_blob_get($blobid, 8192)) {
-				$realblob .= $string;
+		if ($blob_data[0] > $this->maxblobsize) {
+			$realBlob = fbird_blob_get($blobId, $this->maxblobsize);
+			while ($string = fbird_blob_get($blobId, 8192)) {
+				$realBlob .= $string;
 			}
 		} else {
-			$realblob = fbird_blob_get($blobid, $blob_data[0]);
+			$realBlob = fbird_blob_get($blobId, $blob_data[0]);
 		}
 
-		fbird_blob_close( $blobid );
-		return( $realblob );
+		fbird_blob_close($blobId);
+		return $realBlob;
 	}
 
-	function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB')
+	/**
+	 * Insert blob data into a database column directly
+	 * from file
+	 *
+	 * @param string $table table to insert
+	 * @param string $column column to insert
+	 * @param string $path  physical file name
+	 * @param string $where string to find unique record
+	 * @param string $blobtype BLOB or CLOB
+	 *
+	 * @return bool success
+	 */
+	public function updateBlobFile($table,$column,$path,$where,$blobtype='BLOB')
 	{
 		$fd = fopen($path,'rb');
-		if ($fd === false) return false;
+		if ($fd === false)
+			return false;
+
 		$blob_id = fbird_blob_create($this->_connectionID);
 
 		/* fill with data */
@@ -617,105 +832,106 @@ class ADODB_firebird extends ADOConnection {
 		return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false;
 	}
 
-	/*
-		Insert a null into the blob field of the table first.
-		Then use UpdateBlob to store the blob.
-
-		Usage:
-
-		$conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
-		$conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
-	*/
-	function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
-	{
-	$blob_id = fbird_blob_create($this->_connectionID);
-
-	// fbird_blob_add($blob_id, $val);
-
-	// replacement that solves the problem by which only the first modulus 64K /
-	// of $val are stored at the blob field ////////////////////////////////////
-	// Thx Abel Berenstein  aberenstein#afip.gov.ar
-	$len = strlen($val);
-	$chunk_size = 32768;
-	$tail_size = $len % $chunk_size;
-	$n_chunks = ($len - $tail_size) / $chunk_size;
-
-	for ($n = 0; $n < $n_chunks; $n++) {
-		$start = $n * $chunk_size;
-		$data = substr($val, $start, $chunk_size);
-		fbird_blob_add($blob_id, $data);
-	}
-
-	if ($tail_size) {
-		$start = $n_chunks * $chunk_size;
-		$data = substr($val, $start, $tail_size);
-		fbird_blob_add($blob_id, $data);
-	}
-	// end replacement /////////////////////////////////////////////////////////
-
-	$blob_id_str = fbird_blob_close($blob_id);
-
-	return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false;
-
-	}
-
-
-	function OldUpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
+	/**
+	 * Insert blob data into a database column
+	 *
+	 * @param string $table table to insert
+	 * @param string $column column to insert
+	 * @param string $val    value to insert
+	 * @param string $where string to find unique record
+	 * @param string $blobtype BLOB or CLOB
+	 *
+	 * @return bool success
+	 */
+	public function updateBlob($table,$column,$val,$where,$blobtype='BLOB')
 	{
 		$blob_id = fbird_blob_create($this->_connectionID);
-		fbird_blob_add($blob_id, $val);
+
+		// fbird_blob_add($blob_id, $val);
+
+		// replacement that solves the problem by which only the first modulus 64K /
+		// of $val are stored at the blob field ////////////////////////////////////
+		// Thx Abel Berenstein  aberenstein#afip.gov.ar
+		$len = strlen($val);
+		$chunk_size = 32768;
+		$tail_size = $len % $chunk_size;
+		$n_chunks = ($len - $tail_size) / $chunk_size;
+
+		for ($n = 0; $n < $n_chunks; $n++) {
+			$start = $n * $chunk_size;
+			$data = substr($val, $start, $chunk_size);
+			fbird_blob_add($blob_id, $data);
+		}
+
+		if ($tail_size) {
+			$start = $n_chunks * $chunk_size;
+			$data = substr($val, $start, $tail_size);
+			fbird_blob_add($blob_id, $data);
+		}
+		// end replacement /////////////////////////////////////////////////////////
+
 		$blob_id_str = fbird_blob_close($blob_id);
-		return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false;
+
+		return $this->execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false;
+
 	}
 
-	// Format date column in sql string given an input format that understands Y M D
-	// Only since Interbase 6.0 - uses EXTRACT
-	// problem - does not zero-fill the day and month yet
-	function SQLDate($fmt, $col=false)
+
+	/**
+	 * Returns a portably-formatted date string from a timestamp database column.
+	 *
+	 * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:sqldate
+	 *
+	 * Firebird does not support an AM/PM format, so the A indicator always shows AM
+	 *
+	 * @param string $fmt The date format to use.
+	 * @param string|bool $col (Optional) The table column to date format, or if false, use NOW().
+	 *
+	 * @return string The SQL DATE_FORMAT() string, or empty if the provided date format was empty.
+	 */
+	public function sqlDate($fmt, $col=false)
 	{
-		if (!$col) $col = $this->sysDate;
+		if (!$col)
+			$col = 'CURRENT_TIMESTAMP';
+
 		$s = '';
 
 		$len = strlen($fmt);
 		for ($i=0; $i < $len; $i++) {
 			if ($s) $s .= '||';
 			$ch = $fmt[$i];
-			switch($ch) {
+			$choice = strtoupper($ch);
+			switch($choice) {
 			case 'Y':
-			case 'y':
-				$s .= "extract(year from $col)";
+				$s .= "EXTRACT(YEAR FROM $col)";
 				break;
 			case 'M':
-			case 'm':
-				$s .= "extract(month from $col)";
+				$s .= "RIGHT('0' || TRIM(EXTRACT(MONTH FROM $col)),2)";
 				break;
 			case 'W':
-			case 'w':
 				// The more accurate way of doing this is with a stored procedure
 				// See http://wiki.firebirdsql.org/wiki/index.php?page=DATE+Handling+Functions for details
-				$s .= "((extract(yearday from $col) - extract(weekday from $col - 1) + 7) / 7)";
+				$s .= "((EXTRACT(YEARDAY FROM $col) - EXTRACT(WEEKDAY FROM $col - 1) + 7) / 7)";
 				break;
 			case 'Q':
-			case 'q':
-				$s .= "cast(((extract(month from $col)+2) / 3) as integer)";
+				$s .= "CAST(((EXTRACT(MONTH FROM $col)+2) / 3) AS INTEGER)";
 				break;
 			case 'D':
-			case 'd':
-				$s .= "(extract(day from $col))";
+				$s .= "RIGHT('0' || TRIM(EXTRACT(DAY FROM $col)),2)";
 				break;
 			case 'H':
-			case 'h':
-				$s .= "(extract(hour from $col))";
+				$s .= "RIGHT('0' || TRIM(EXTRACT(HOUR FROM $col)),2)";
 				break;
 			case 'I':
-			case 'i':
-				$s .= "(extract(minute from $col))";
+				$s .= "RIGHT('0' || TRIM(EXTRACT(MINUTE FROM $col)),2)";
 				break;
 			case 'S':
-			case 's':
-				$s .= "CAST((extract(second from $col)) AS INTEGER)";
+				//$s .= "CAST((EXTRACT(SECOND FROM $col)) AS INTEGER)";
+				$s .= "RIGHT('0' || TRIM(EXTRACT(SECOND FROM $col)),2)";
+				break;
+			case 'A':
+				$s .= $this->qstr('AM');
 				break;
-
 			default:
 				if ($ch == '\\') {
 					$i++;
@@ -728,10 +944,44 @@ class ADODB_firebird extends ADOConnection {
 		return $s;
 	}
 
+	/**
+	 * Creates a portable date offset field, for use in SQL statements.
+	 *
+	 * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:offsetdate
+	 *
+	 * @param float $dayFraction A day in floating point
+	 * @param string|bool $date (Optional) The date to offset. If false, uses CURDATE()
+	 *
+	 * @return string
+	 */
+	public function offsetDate($dayFraction, $date=false)
+	{
+		if (!$date)
+			$date = $this->sysTimeStamp;
+
+		$fraction = $dayFraction * 24 * 3600;
+		return sprintf("DATEADD (second, %s, %s)  FROM RDB\$DATABASE",$fraction,$date);
+	}
+
+
 	// Note that Interbase 6.5 uses this ROWS instead - don't you love forking wars!
 	// 		SELECT col1, col2 FROM table ROWS 5 -- get 5 rows
 	//		SELECT col1, col2 FROM TABLE ORDER BY col1 ROWS 3 TO 7 -- first 5 skip 2
-	function &SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false, $secs=0)
+	/**
+	 * Executes a provided SQL statement and returns a handle to the result, with the ability to supply a starting
+	 * offset and record count.
+	 *
+	 * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:selectlimit
+	 *
+	 * @param string $sql The SQL to execute.
+	 * @param int $nrows (Optional) The limit for the number of records you want returned. By default, all results.
+	 * @param int $offset (Optional) The offset to use when selecting the results. By default, no offset.
+	 * @param array|bool $inputarr (Optional) Any parameter values required by the SQL statement, or false if none.
+	 * @param int $secs2cache (Optional) If greater than 0, perform a cached execute. By default, normal execution.
+	 *
+	 * @return ADORecordSet|false The query results, or false if the query failed to execute.
+	 */
+	public function selectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false, $secs2cache=0)
 	{
 		$nrows = (integer) $nrows;
 		$offset = (integer) $offset;
@@ -740,46 +990,88 @@ class ADODB_firebird extends ADOConnection {
 		$str .=($offset>=0) ? "SKIP $offset " : '';
 
 		$sql = preg_replace('/^[ \t]*select/i',$str,$sql);
-		if ($secs)
-			$rs = $this->CacheExecute($secs,$sql,$inputarr);
+		if ($secs2cache)
+			$rs = $this->cacheExecute($secs2cache,$sql,$inputarr);
 		else
-			$rs = $this->Execute($sql,$inputarr);
+			$rs = $this->execute($sql,$inputarr);
 
 		return $rs;
 	}
 
 }
 
-/*--------------------------------------------------------------------------------------
-		 Class Name: Recordset
---------------------------------------------------------------------------------------*/
-
-class  ADORecordset_firebird extends ADORecordSet
+/**
+ * Class ADORecordset_firebird
+ */
+class ADORecordset_firebird extends ADORecordSet
 {
-
 	var $databaseType = "firebird";
-	var $bind=false;
-	var $_cacheType;
-
-	function __construct($id,$mode=false)
-	{
-	global $ADODB_FETCH_MODE;
-
-			$this->fetchMode = ($mode === false) ? $ADODB_FETCH_MODE : $mode;
-			parent::__construct($id);
-	}
+	var $bind = false;
 
 	/**
-	 * 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.
-	 * @return object containing field information.
-	*/
-	function FetchField($fieldOffset = -1)
+	 * @var ADOFieldObject[] Holds a cached version of the metadata
+	 */
+	private $fieldObjects = false;
+
+	/**
+	 * @var bool Flags if we have retrieved the metadata
+	 */
+	private $fieldObjectsRetrieved = false;
+
+	/**
+	 * @var array Cross-reference the objects by name for easy access
+	 */
+	private $fieldObjectsIndex = array();
+
+	/**
+	 * @var bool Flag to indicate if the result has a blob
+	 */
+	private $fieldObjectsHaveBlob = false;
+
+	function __construct($id, $mode = false)
 	{
+		global $ADODB_FETCH_MODE;
+
+		$this->fetchMode = ($mode === false) ? $ADODB_FETCH_MODE : $mode;
+		parent::__construct($id);
+	}
+
+
+	/**
+	 * 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.
+	 *
+	 * $param int $fieldOffset (optional default=-1 for all
+	 * @return mixed an ADOFieldObject, or array of objects
+	 */
+	private function _fetchField($fieldOffset = -1)
+	{
+		if ($this->fieldObjectsRetrieved) {
+			if ($this->fieldObjects) {
+				// Already got the information
+				if ($fieldOffset == -1) {
+					return $this->fieldObjects;
+				} else {
+					return $this->fieldObjects[$fieldOffset];
+				}
+			} else {
+				// No metadata available
+				return false;
+			}
+		}
+
+		// Populate the field objects cache
+		$this->fieldObjectsRetrieved = true;
+		$this->fieldObjectsHaveBlob = false;
+		$this->_numOfFields = fbird_num_fields($this->_queryID);
+		for ($fieldIndex = 0; $fieldIndex < $this->_numOfFields; $fieldIndex++) {
 			$fld = new ADOFieldObject;
-			 $ibf = fbird_field_info($this->_queryID,$fieldOffset);
+			$ibf = fbird_field_info($this->_queryID, $fieldIndex);
 
 			$name = empty($ibf['alias']) ? $ibf['name'] : $ibf['alias'];
 
@@ -799,23 +1091,54 @@ class  ADORecordset_firebird extends ADORecordSet
 			$fld->type = $ibf['type'];
 			$fld->max_length = $ibf['length'];
 
-			/*       This needs to be populated from the metadata */
+			// This needs to be populated from the metadata
 			$fld->not_null = false;
 			$fld->has_default = false;
 			$fld->default_value = 'null';
-			return $fld;
+
+			$this->fieldObjects[$fieldIndex] = $fld;
+			$this->fieldObjectsIndex[$fld->name] = $fieldIndex;
+
+			if ($fld->type == 'BLOB') {
+				$this->fieldObjectsHaveBlob = true;
+			}
+		}
+
+		if ($fieldOffset == -1) {
+			return $this->fieldObjects;
+		}
+
+		return $this->fieldObjects[$fieldOffset];
+	}
+
+	/**
+	 * Fetchfield copies the oracle method, it loads the field information
+	 * into the _fieldobjs array once, to save multiple calls to the
+	 * fbird_ function
+	 *
+	 * @param int $fieldOffset (optional)
+	 *
+	 * @return adoFieldObject|false
+	 */
+	public function fetchField($fieldOffset = -1)
+	{
+		if ($fieldOffset == -1) {
+			return $this->fieldObjects;
+		}
+
+		return $this->fieldObjects[$fieldOffset];
 	}
 
 	function _initrs()
 	{
 		$this->_numOfRows = -1;
-		$this->_numOfFields = @fbird_num_fields($this->_queryID);
 
-		// cache types for blob decode check
-		for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) {
-			$f1 = $this->FetchField($i);
-			$this->_cacheType[] = $f1->type;
-		}
+		/*
+		* Retrieve all of the column information first. We copy
+		* this method from oracle
+		*/
+		$this->_fetchField();
+
 	}
 
 	function _seek($row)
@@ -823,98 +1146,171 @@ class  ADORecordset_firebird extends ADORecordSet
 		return false;
 	}
 
-	function _fetch()
+	public function _fetch()
 	{
-		$f = @fbird_fetch_row($this->_queryID);
+		// Case conversion function for use in Closure defined below
+		$localFnCaseConv = null;
+
+		if ($this->fetchMode & ADODB_FETCH_ASSOC) {
+			// Handle either associative or fetch both
+			$localNumeric = false;
+
+			$f = @fbird_fetch_assoc($this->_queryID);
+			if (is_array($f)) {
+				// Optimally do the case_upper or case_lower
+				if (ADODB_ASSOC_CASE == ADODB_ASSOC_CASE_LOWER) {
+					$f = array_change_key_case($f, CASE_LOWER);
+					$localFnCaseConv = 'strtolower';
+				} elseif (ADODB_ASSOC_CASE == ADODB_ASSOC_CASE_UPPER) {
+					$f = array_change_key_case($f, CASE_UPPER);
+					$localFnCaseConv = 'strtoupper';
+				}
+			}
+		} else {
+			// Numeric fetch mode
+			$localNumeric = true;
+			$f = @fbird_fetch_row($this->_queryID);
+		}
+
 		if ($f === false) {
 			$this->fields = false;
 			return false;
 		}
+
 		// OPN stuff start - optimized
 		// fix missing nulls and decode blobs automatically
-
 		global $ADODB_ANSI_PADDING_OFF;
-		//$ADODB_ANSI_PADDING_OFF=1;
 		$rtrim = !empty($ADODB_ANSI_PADDING_OFF);
 
-		for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) {
-			if ($this->_cacheType[$i]=="BLOB") {
-				if (isset($f[$i])) {
-					$f[$i] = $this->connection->_BlobDecode($f[$i]);
+		// For optimal performance, only process if there is a possibility of something to do
+		if ($this->fieldObjectsHaveBlob || $rtrim) {
+			$localFieldObjects = $this->fieldObjects;
+			$localFieldObjectIndex = $this->fieldObjectsIndex;
+			/** @var ADODB_firebird $localConnection */
+			$localConnection = &$this->connection;
+
+			/**
+			 * Closure for an efficient method of iterating over the elements.
+			 * @param mixed      $value
+			 * @param string|int $key
+			 * @return void
+			 */
+			$rowTransform = function ($value, $key) use (
+				&$f,
+				$rtrim,
+				$localFieldObjects,
+				$localConnection,
+				$localNumeric,
+				$localFnCaseConv,
+				$localFieldObjectIndex
+			) {
+				if ($localNumeric) {
+					$localKey = $key;
 				} else {
-					$f[$i] = null;
+					// Cross-reference the associative key back to numeric
+					// with appropriate case conversion
+					$index = $localFnCaseConv ? $localFnCaseConv($key) : $key;
+					$localKey = $localFieldObjectIndex[$index];
 				}
-			} else {
-				if (!isset($f[$i])) {
-					$f[$i] = null;
-				} else if ($rtrim && is_string($f[$i])) {
-					$f[$i] = rtrim($f[$i]);
+
+				// As we iterate the elements check for blobs and padding
+				if ($localFieldObjects[$localKey]->type == 'BLOB') {
+					$f[$key] = $localConnection->_BlobDecode($value);
+				} else {
+					if ($rtrim && is_string($value)) {
+						$f[$key] = rtrim($value);
+					}
 				}
-			}
+
+			};
+
+			// Walk the array, applying the above closure
+			array_walk($f, $rowTransform);
+		}
+
+		if (!$localNumeric && $this->fetchMode & ADODB_FETCH_NUM) {
+			// Creates a fetch both
+			$fNum = array_values($f);
+			$f = array_merge($f, $fNum);
 		}
-		// OPN stuff end
 
 		$this->fields = $f;
-		if ($this->fetchMode == ADODB_FETCH_ASSOC) {
-			$this->fields = $this->GetRowAssoc();
-		} else if ($this->fetchMode == ADODB_FETCH_BOTH) {
-			$this->fields = array_merge($this->fields,$this->GetRowAssoc());
-		}
+
 		return true;
 	}
 
-	/* Use associative array to get fields array */
-	function Fields($colname)
+	/**
+	 * Get the value of a field in the current row by column name.
+	 * Will not work if ADODB_FETCH_MODE is set to ADODB_FETCH_NUM.
+	 *
+	 * @param string $colname is the field to access
+	 *
+	 * @return mixed the value of $colname column
+	 */
+	public function fields($colname)
 	{
-		if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname];
+		if ($this->fetchMode & ADODB_FETCH_ASSOC) {
+			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;
-			}
+			// fieldsObjectIndex populated by the recordset load
+			$this->bind = array_change_key_case($this->fieldObjectsIndex, CASE_UPPER);
 		}
 
 		return $this->fields[$this->bind[strtoupper($colname)]];
-
 	}
 
 
 	function _close()
 	{
-			return @fbird_free_result($this->_queryID);
+		return @fbird_free_result($this->_queryID);
 	}
 
-	function MetaType($t,$len=-1,$fieldobj=false)
+	public function metaType($t, $len = -1, $fieldObj = false)
 	{
 		if (is_object($t)) {
-			$fieldobj = $t;
-			$t = $fieldobj->type;
-			$len = $fieldobj->max_length;
+			$fieldObj = $t;
+			$t = $fieldObj->type;
+			$len = $fieldObj->max_length;
 		}
-		switch (strtoupper($t)) {
-		case 'CHAR':
-			return 'C';
 
-		case 'TEXT':
-		case 'VARCHAR':
-		case 'VARYING':
-		if ($len <= $this->blobSize) return 'C';
-			return 'X';
-		case 'BLOB':
-			return 'B';
+		$t = strtoupper($t);
 
-		case 'TIMESTAMP':
-		case 'DATE': return 'D';
-		case 'TIME': return 'T';
-				//case 'T': return 'T';
+		if (array_key_exists($t, $this->connection->customActualTypes)) {
+			return $this->connection->customActualTypes[$t];
+		}
 
-				//case 'L': return 'L';
-		case 'INT':
-		case 'SHORT':
-		case 'INTEGER': return 'I';
-		default: return ADODB_DEFAULT_METATYPE;
+		switch ($t) {
+			case 'CHAR':
+				return 'C';
+
+			case 'TEXT':
+			case 'VARCHAR':
+			case 'VARYING':
+				if ($len <= $this->blobSize) {
+					return 'C';
+				}
+				return 'X';
+			case 'BLOB':
+				return 'B';
+
+			case 'TIMESTAMP':
+			case 'DATE':
+				return 'D';
+			case 'TIME':
+				return 'T';
+			//case 'T': return 'T';
+
+			//case 'L': return 'L';
+			case 'INT':
+			case 'SHORT':
+			case 'INTEGER':
+				return 'I';
+			default:
+				return ADODB_DEFAULT_METATYPE;
 		}
 	}
 
 }
+
diff --git a/lib/adodb/drivers/adodb-ibase.inc.php b/lib/adodb/drivers/adodb-ibase.inc.php
index 69de30dcca7..81ab29b56ec 100644
--- a/lib/adodb/drivers/adodb-ibase.inc.php
+++ b/lib/adodb/drivers/adodb-ibase.inc.php
@@ -860,7 +860,14 @@ class ADORecordset_ibase extends ADORecordSet
 			$t = $fieldobj->type;
 			$len = $fieldobj->max_length;
 		}
-		switch (strtoupper($t)) {
+		
+		$t = strtoupper($t);
+		
+		if (array_key_exists($t,$this->connection->customActualTypes))
+			return  $this->connection->customActualTypes[$t];
+
+		switch ($t) {
+			
 		case 'CHAR':
 			return 'C';
 
diff --git a/lib/adodb/drivers/adodb-informix72.inc.php b/lib/adodb/drivers/adodb-informix72.inc.php
index a9c43e2318a..79e5138a9ce 100644
--- a/lib/adodb/drivers/adodb-informix72.inc.php
+++ b/lib/adodb/drivers/adodb-informix72.inc.php
@@ -253,12 +253,12 @@ class ADODB_informix72 extends ADOConnection {
 		return $false;
 	}
 
-   function xMetaColumns($table)
-   {
+	function xMetaColumns($table)
+	{
 		return ADOConnection::MetaColumns($table,false);
-   }
+	}
 
-	 function MetaForeignKeys($table, $owner=false, $upper=false) //!Eos
+	public function metaForeignKeys($table, $owner = '', $upper = false, $associative = false)
 	{
 		$sql = "
 			select tr.tabname,updrule,delrule,
diff --git a/lib/adodb/drivers/adodb-mssql.inc.php b/lib/adodb/drivers/adodb-mssql.inc.php
index 3de3f8d33ce..8fb92249ccd 100644
--- a/lib/adodb/drivers/adodb-mssql.inc.php
+++ b/lib/adodb/drivers/adodb-mssql.inc.php
@@ -429,7 +429,7 @@ class ADODB_mssql extends ADOConnection {
 		return $indexes;
 	}
 
-	function MetaForeignKeys($table, $owner=false, $upper=false)
+	public function metaForeignKeys($table, $owner = '', $upper = false, $associative = false)
 	{
 	global $ADODB_FETCH_MODE;
 
diff --git a/lib/adodb/drivers/adodb-mssqlnative.inc.php b/lib/adodb/drivers/adodb-mssqlnative.inc.php
index 36dc543396a..89eb42ed763 100644
--- a/lib/adodb/drivers/adodb-mssqlnative.inc.php
+++ b/lib/adodb/drivers/adodb-mssqlnative.inc.php
@@ -151,6 +151,8 @@ class ADODB_mssqlnative extends ADOConnection {
 
 		$arrServerInfo = sqlsrv_server_info($this->_connectionID);
 		$ADODB_FETCH_MODE = $savem;
+		
+		$arr = array();
 		$arr['description'] = $arrServerInfo['SQLServerName'].' connected to '.$arrServerInfo['CurrentDatabase'];
 		$arr['version'] = $arrServerInfo['SQLServerVersion'];//ADOConnection::_findvers($arr['description']);
 		return $arr;
@@ -719,7 +721,7 @@ class ADODB_mssqlnative extends ADOConnection {
 		return $indexes;
 	}
 
-	function MetaForeignKeys($table, $owner=false, $upper=false)
+	public function metaForeignKeys($table, $owner = '', $upper = false, $associative = false)
 	{
 		global $ADODB_FETCH_MODE;
 
@@ -1009,6 +1011,37 @@ class ADODB_mssqlnative extends ADOConnection {
 		return $metaProcedures;
 	}
 
+	/**
+	* An SQL Statement that adds a specific number of
+	* days or part to local datetime
+	*
+	* @param float $dayFraction
+	* @param string $date
+	*
+	* @return string
+	*/
+	public function offsetDate($dayFraction, $date = false)
+	{
+		if (!$date)
+			/*
+			* Use GETDATE() via systTimestamp;
+			*/
+			$date = $this->sysTimeStamp;
+
+		/*
+		* seconds, number of seconds, date base
+		*/
+		$dateFormat = "DATEADD(s, %s, %s)";
+
+		/*
+		* Adjust the offset back to seconds
+		*/
+		$fraction = $dayFraction * 24 * 3600;
+
+		return sprintf($dateFormat,$fraction,$date);
+
+	}
+
 }
 
 /*--------------------------------------------------------------------------------------
diff --git a/lib/adodb/drivers/adodb-mysql.inc.php b/lib/adodb/drivers/adodb-mysql.inc.php
deleted file mode 100644
index f07c081b527..00000000000
--- a/lib/adodb/drivers/adodb-mysql.inc.php
+++ /dev/null
@@ -1,932 +0,0 @@
-=')) {
-			$this->outp_throw(
-				'mysql extension is not supported since PHP 7.0.0, use mysqli instead',
-				__METHOD__
-			);
-			die(1); // Stop execution even if not using Exceptions
-		} elseif(version_compare(PHP_VERSION, '5.5.0', '>=')) {
-			// If mysql extension is available just print a warning,
-			// otherwise die with an error message
-			if(function_exists('mysql_connect')) {
-				$this->outp('mysql extension is deprecated since PHP 5.5.0, consider using mysqli');
-			} else {
-				$this->outp_throw(
-					'mysql extension is not available, use mysqli instead',
-					__METHOD__
-				);
-				die(1); // Stop execution even if not using Exceptions
-			}
-		}
-	}
-
-	function setCharSet($charset)
-	{
-		if (!function_exists('mysql_set_charset')) {
-			return false;
-		}
-
-		if ($this->charSet !== $charset) {
-			$ok = @mysql_set_charset($charset,$this->_connectionID);
-			if ($ok) {
-				$this->charSet = $charset;
-				return true;
-			}
-			return false;
-		}
-		return true;
-	}
-
-	function serverInfo()
-	{
-		$arr['description'] = ADOConnection::GetOne("select version()");
-		$arr['version'] = ADOConnection::_findvers($arr['description']);
-		return $arr;
-	}
-
-	function ifNull( $field, $ifNull )
-	{
-		return " IFNULL($field, $ifNull) "; // if MySQL
-	}
-
-	function metaProcedures($NamePattern = false, $catalog = null, $schemaPattern = null)
-	{
-		// save old fetch mode
-		global $ADODB_FETCH_MODE;
-
-		$false = false;
-		$save = $ADODB_FETCH_MODE;
-		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
-
-		if ($this->fetchMode !== FALSE) {
-			$savem = $this->SetFetchMode(FALSE);
-		}
-
-		$procedures = array ();
-
-		// get index details
-
-		$likepattern = '';
-		if ($NamePattern) {
-			$likepattern = " LIKE '".$NamePattern."'";
-		}
-		$rs = $this->Execute('SHOW PROCEDURE STATUS'.$likepattern);
-		if (is_object($rs)) {
-
-			// parse index data into array
-			while ($row = $rs->FetchRow()) {
-				$procedures[$row[1]] = array(
-					'type' => 'PROCEDURE',
-					'catalog' => '',
-					'schema' => '',
-					'remarks' => $row[7],
-				);
-			}
-		}
-
-		$rs = $this->Execute('SHOW FUNCTION STATUS'.$likepattern);
-		if (is_object($rs)) {
-			// parse index data into array
-			while ($row = $rs->FetchRow()) {
-				$procedures[$row[1]] = array(
-					'type' => 'FUNCTION',
-					'catalog' => '',
-					'schema' => '',
-					'remarks' => $row[7]
-				);
-			}
-		}
-
-		// restore fetchmode
-		if (isset($savem)) {
-			$this->SetFetchMode($savem);
-		}
-		$ADODB_FETCH_MODE = $save;
-
-		return $procedures;
-	}
-
-	/**
-	 * Retrieves a list of tables based on given criteria
-	 *
-	 * @param string $ttype Table type = 'TABLE', 'VIEW' or false=both (default)
-	 * @param string $showSchema schema name, false = current schema (default)
-	 * @param string $mask filters the table by name
-	 *
-	 * @return array list of tables
-	 */
-	function metaTables($ttype=false,$showSchema=false,$mask=false)
-	{
-		$save = $this->metaTablesSQL;
-		if ($showSchema && is_string($showSchema)) {
-			$this->metaTablesSQL .= $this->qstr($showSchema);
-		} else {
-			$this->metaTablesSQL .= "schema()";
-		}
-
-		if ($mask) {
-			$mask = $this->qstr($mask);
-			$this->metaTablesSQL .= " AND table_name LIKE $mask";
-		}
-		$ret = ADOConnection::MetaTables($ttype,$showSchema);
-
-		$this->metaTablesSQL = $save;
-		return $ret;
-	}
-
-
-	function metaIndexes ($table, $primary = FALSE, $owner=false)
-	{
-		// save old fetch mode
-		global $ADODB_FETCH_MODE;
-
-		$false = false;
-		$save = $ADODB_FETCH_MODE;
-		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
-		if ($this->fetchMode !== FALSE) {
-			$savem = $this->SetFetchMode(FALSE);
-		}
-
-		// get index details
-		$rs = $this->Execute(sprintf('SHOW INDEX FROM %s',$table));
-
-		// restore fetchmode
-		if (isset($savem)) {
-			$this->SetFetchMode($savem);
-		}
-		$ADODB_FETCH_MODE = $save;
-
-		if (!is_object($rs)) {
-			return $false;
-		}
-
-		$indexes = array ();
-
-		// parse index data into array
-		while ($row = $rs->FetchRow()) {
-			if ($primary == FALSE AND $row[2] == 'PRIMARY') {
-				continue;
-			}
-
-			if (!isset($indexes[$row[2]])) {
-				$indexes[$row[2]] = array(
-					'unique' => ($row[1] == 0),
-					'columns' => array()
-				);
-			}
-
-			$indexes[$row[2]]['columns'][$row[3] - 1] = $row[4];
-		}
-
-		// sort columns by order in the index
-		foreach ( array_keys ($indexes) as $index )
-		{
-			ksort ($indexes[$index]['columns']);
-		}
-
-		return $indexes;
-	}
-
-
-	/**
-	 * Appropriately quotes strings with ' characters for insertion into the database.
-	 *
-	 * Relies on mysql_real_escape_string()
-	 * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:qstr
-	 *
-	 * @param string $s            The string to quote
-	 * @param bool   $magic_quotes This param is not used since 5.21.0.
-	 *                             It remains for backwards compatibility.
-	 *
-	 * @return string Quoted string
-	 */
-	function qStr($s, $magic_quotes=false)
-	{
-		if (is_null($s)) {
-			return 'NULL';
-		}
-
-		if (is_resource($this->_connectionID)) {
-			return "'" . mysql_real_escape_string($s, $this->_connectionID) . "'";
-		}
-
-		if ($this->replaceQuote[0] == '\\') {
-			$s = str_replace(array('\\', "\0"), array('\\\\', "\\\0"), $s);
-		}
-		return "'" . str_replace("'", $this->replaceQuote, $s) . "'";
-	}
-
-	protected function _insertID($table = '', $column = '')
-	{
-		return ADOConnection::GetOne('SELECT LAST_INSERT_ID()');
-		//return mysql_insert_id($this->_connectionID);
-	}
-
-	function getOne($sql,$inputarr=false)
-	{
-	global $ADODB_GETONE_EOF;
-		if ($this->compat323 == false && strncasecmp($sql,'sele',4) == 0) {
-			$rs = $this->SelectLimit($sql,1,-1,$inputarr);
-			if ($rs) {
-				$rs->Close();
-				if ($rs->EOF) return $ADODB_GETONE_EOF;
-				return reset($rs->fields);
-			}
-		} else {
-			return ADOConnection::GetOne($sql,$inputarr);
-		}
-		return false;
-	}
-
-	function beginTrans()
-	{
-		if ($this->debug) ADOConnection::outp("Transactions not supported in 'mysql' driver. Use 'mysqlt' or 'mysqli' driver");
-	}
-
-	function _affectedrows()
-	{
-			return mysql_affected_rows($this->_connectionID);
-	}
-
-	 // See http://www.mysql.com/doc/M/i/Miscellaneous_functions.html
-	// Reference on Last_Insert_ID on the recommended way to simulate sequences
-	var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);";
-	var $_genSeqSQL = "create table if not exists %s (id int not null)";
-	var $_genSeqCountSQL = "select count(*) from %s";
-	var $_genSeq2SQL = "insert into %s values (%s)";
-	var $_dropSeqSQL = "drop table if exists %s";
-
-	function createSequence($seqname='adodbseq',$startID=1)
-	{
-		if (empty($this->_genSeqSQL)) return false;
-		$u = strtoupper($seqname);
-
-		$ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname));
-		if (!$ok) return false;
-		return $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1));
-	}
-
-
-	function genID($seqname='adodbseq',$startID=1)
-	{
-		// post-nuke sets hasGenID to false
-		if (!$this->hasGenID) return false;
-
-		$savelog = $this->_logsql;
-		$this->_logsql = false;
-		$getnext = sprintf($this->_genIDSQL,$seqname);
-		$holdtransOK = $this->_transOK; // save the current status
-		$rs = @$this->Execute($getnext);
-		if (!$rs) {
-			if ($holdtransOK) $this->_transOK = true; //if the status was ok before reset
-			$u = strtoupper($seqname);
-			$this->Execute(sprintf($this->_genSeqSQL,$seqname));
-			$cnt = $this->GetOne(sprintf($this->_genSeqCountSQL,$seqname));
-			if (!$cnt) $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1));
-			$rs = $this->Execute($getnext);
-		}
-
-		if ($rs) {
-			$this->genID = mysql_insert_id($this->_connectionID);
-			$rs->Close();
-		} else
-			$this->genID = 0;
-
-		$this->_logsql = $savelog;
-		return $this->genID;
-	}
-
-	function metaDatabases()
-	{
-		$qid = mysql_list_dbs($this->_connectionID);
-		$arr = array();
-		$i = 0;
-		$max = mysql_num_rows($qid);
-		while ($i < $max) {
-			$db = mysql_tablename($qid,$i);
-			if ($db != 'mysql') $arr[] = $db;
-			$i += 1;
-		}
-		return $arr;
-	}
-
-
-	// 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 = 'DATE_FORMAT('.$col.",'";
-		$concat = false;
-		$len = strlen($fmt);
-		for ($i=0; $i < $len; $i++) {
-			$ch = $fmt[$i];
-			switch($ch) {
-
-			default:
-				if ($ch == '\\') {
-					$i++;
-					$ch = substr($fmt,$i,1);
-				}
-				/** FALL THROUGH */
-			case '-':
-			case '/':
-				$s .= $ch;
-				break;
-
-			case 'Y':
-			case 'y':
-				$s .= '%Y';
-				break;
-			case 'M':
-				$s .= '%b';
-				break;
-
-			case 'm':
-				$s .= '%m';
-				break;
-			case 'D':
-			case 'd':
-				$s .= '%d';
-				break;
-
-			case 'Q':
-			case 'q':
-				$s .= "'),Quarter($col)";
-
-				if ($len > $i+1) $s .= ",DATE_FORMAT($col,'";
-				else $s .= ",('";
-				$concat = true;
-				break;
-
-			case 'H':
-				$s .= '%H';
-				break;
-
-			case 'h':
-				$s .= '%I';
-				break;
-
-			case 'i':
-				$s .= '%i';
-				break;
-
-			case 's':
-				$s .= '%s';
-				break;
-
-			case 'a':
-			case 'A':
-				$s .= '%p';
-				break;
-
-			case 'w':
-				$s .= '%w';
-				break;
-
-			 case 'W':
-				$s .= '%U';
-				break;
-
-			case 'l':
-				$s .= '%W';
-				break;
-			}
-		}
-		$s.="')";
-		if ($concat) $s = "CONCAT($s)";
-		return $s;
-	}
-
-
-	// returns concatenated string
-	// much easier to run "mysqld --ansi" or "mysqld --sql-mode=PIPES_AS_CONCAT" and use || operator
-	function concat()
-	{
-		$s = "";
-		$arr = func_get_args();
-
-		// suggestion by andrew005@mnogo.ru
-		$s = implode(',',$arr);
-		if (strlen($s) > 0) return "CONCAT($s)";
-		else return '';
-	}
-
-	function offsetDate($dayFraction,$date=false)
-	{
-		if (!$date) $date = $this->sysDate;
-
-		$fraction = $dayFraction * 24 * 3600;
-		return '('. $date . ' + INTERVAL ' .	 $fraction.' SECOND)';
-
-//		return "from_unixtime(unix_timestamp($date)+$fraction)";
-	}
-
-	// returns true or false
-	function _connect($argHostname, $argUsername, $argPassword, $argDatabasename)
-	{
-		if (!empty($this->port))
-			$argHostname .= ":".$this->port;
-
-		$this->_connectionID =
-			mysql_connect($argHostname,
-						  $argUsername,
-						  $argPassword,
-						  $this->forceNewConnect,
-						  $this->clientFlags
-						  );
-
-
-		if ($this->_connectionID === false)
-			return false;
-		if ($argDatabasename)
-			return $this->SelectDB($argDatabasename);
-
-		return true;
-	}
-
-	// returns true or false
-	function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
-	{
-		if (!empty($this->port)) $argHostname .= ":".$this->port;
-
-		$this->_connectionID =
-			mysql_pconnect($argHostname,
-						   $argUsername,
-						   $argPassword,
-						   $this->clientFlags);
-
-		if ($this->_connectionID === false)
-			return false;
-		if ($this->autoRollback)
-			$this->RollbackTrans();
-		if ($argDatabasename)
-			return $this->SelectDB($argDatabasename);
-		return true;
-	}
-
-	function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
-	{
-		$this->forceNewConnect = true;
-		return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename);
-	}
-
-	function metaColumns($table, $normalize=true)
-	{
-		$this->_findschema($table,$schema);
-		if ($schema) {
-			$dbName = $this->database;
-			$this->SelectDB($schema);
-		}
-		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(sprintf($this->metaColumnsSQL,$table));
-
-		if ($schema) {
-			$this->SelectDB($dbName);
-		}
-
-		if (isset($savem)) $this->SetFetchMode($savem);
-		$ADODB_FETCH_MODE = $save;
-		if (!is_object($rs)) {
-			$false = false;
-			return $false;
-		}
-
-		$retarr = array();
-		while (!$rs->EOF){
-			$fld = new ADOFieldObject();
-			$fld->name = $rs->fields[0];
-			$type = $rs->fields[1];
-
-			// split type into type(length):
-			$fld->scale = null;
-			if (preg_match("/^(.+)\((\d+),(\d+)/", $type, $query_array)) {
-				$fld->type = $query_array[1];
-				$fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1;
-				$fld->scale = is_numeric($query_array[3]) ? $query_array[3] : -1;
-			} elseif (preg_match("/^(.+)\((\d+)/", $type, $query_array)) {
-				$fld->type = $query_array[1];
-				$fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1;
-			} elseif (preg_match("/^(enum)\((.*)\)$/i", $type, $query_array)) {
-				$fld->type = $query_array[1];
-				$arr = explode(",",$query_array[2]);
-				$fld->enums = $arr;
-				$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;
-			}
-			$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 || strpos($type,'binary') !== false);
-			$fld->unsigned = (strpos($type,'unsigned') !== false);
-			$fld->zerofill = (strpos($type,'zerofill') !== false);
-
-			if (!$fld->binary) {
-				$d = $rs->fields[4];
-				if ($d != '' && $d != 'NULL') {
-					$fld->has_default = true;
-					$fld->default_value = $d;
-				} else {
-					$fld->has_default = false;
-				}
-			}
-
-			if ($save == ADODB_FETCH_NUM) {
-				$retarr[] = $fld;
-			} else {
-				$retarr[strtoupper($fld->name)] = $fld;
-			}
-				$rs->MoveNext();
-			}
-
-			$rs->Close();
-			return $retarr;
-	}
-
-	// returns true or false
-	function selectDB($dbName)
-	{
-		$this->database = $dbName;
-		$this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions
-		if ($this->_connectionID) {
-			return @mysql_select_db($dbName,$this->_connectionID);
-		}
-		else return false;
-	}
-
-	// parameters use PostgreSQL convention, not MySQL
-	function selectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs=0)
-	{
-		$nrows = (int) $nrows;
-		$offset = (int) $offset;
-		$offsetStr =($offset>=0) ? ((integer)$offset)."," : '';
-		// jason judge, see PHPLens Issue No: 9220
-		if ($nrows < 0) $nrows = '18446744073709551615';
-
-		if ($secs)
-			$rs = $this->CacheExecute($secs,$sql." LIMIT $offsetStr".((integer)$nrows),$inputarr);
-		else
-			$rs = $this->Execute($sql." LIMIT $offsetStr".((integer)$nrows),$inputarr);
-		return $rs;
-	}
-
-	// returns queryID or false
-	function _query($sql,$inputarr=false)
-	{
-
-	return mysql_query($sql,$this->_connectionID);
-	/*
-	global $ADODB_COUNTRECS;
-		if($ADODB_COUNTRECS)
-			return mysql_query($sql,$this->_connectionID);
-		else
-			return @mysql_unbuffered_query($sql,$this->_connectionID); // requires PHP >= 4.0.6
-	*/
-	}
-
-	/*	Returns: the last error message from previous database operation	*/
-	function errorMsg()
-	{
-
-		if ($this->_logsql) return $this->_errorMsg;
-		if (empty($this->_connectionID)) $this->_errorMsg = @mysql_error();
-		else $this->_errorMsg = @mysql_error($this->_connectionID);
-		return $this->_errorMsg;
-	}
-
-	/*	Returns: the last error number from previous database operation	*/
-	function errorNo()
-	{
-		if ($this->_logsql) return $this->_errorCode;
-		if (empty($this->_connectionID)) return @mysql_errno();
-		else return @mysql_errno($this->_connectionID);
-	}
-
-	// returns true or false
-	function _close()
-	{
-		@mysql_close($this->_connectionID);
-
-		$this->charSet = '';
-		$this->_connectionID = false;
-	}
-
-
-	/*
-	* Maximum size of C field
-	*/
-	function charMax()
-	{
-		return 255;
-	}
-
-	/*
-	* Maximum size of X field
-	*/
-	function textMax()
-	{
-		return 4294967295;
-	}
-
-	// "Innox - Juan Carlos Gonzalez" 
-	function metaForeignKeys( $table, $owner = FALSE, $upper = FALSE, $associative = FALSE )
-	{
-	 global $ADODB_FETCH_MODE;
-		if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC || $this->fetchMode == ADODB_FETCH_ASSOC) $associative = true;
-
-		if ( !empty($owner) ) {
-			$table = "$owner.$table";
-		}
-		$a_create_table = $this->getRow(sprintf('SHOW CREATE TABLE %s', $table));
-		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();
-
-		if (!preg_match_all("/FOREIGN KEY \(`(.*?)`\) REFERENCES `(.*?)` \(`(.*?)`\)/", $create_sql, $matches)) return false;
-		$foreign_keys = array();
-		$num_keys = count($matches[0]);
-		for ( $i = 0; $i < $num_keys; $i ++ ) {
-			$my_field  = explode('`, `', $matches[1][$i]);
-			$ref_table = $matches[2][$i];
-			$ref_field = explode('`, `', $matches[3][$i]);
-
-			if ( $upper ) {
-				$ref_table = strtoupper($ref_table);
-			}
-
-			// see https://sourceforge.net/p/adodb/bugs/100/
-			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 {
-					$foreign_keys[$ref_table][] = "{$my_field[$j]}={$ref_field[$j]}";
-				}
-			}
-		}
-
-		return $foreign_keys;
-	}
-
-
-}
-
-/*--------------------------------------------------------------------------------------
-	 Class Name: Recordset
---------------------------------------------------------------------------------------*/
-
-
-class ADORecordSet_mysql extends ADORecordSet{
-
-	var $databaseType = "mysql";
-	var $canSeek = true;
-
-	function __construct($queryID,$mode=false)
-	{
-		if ($mode === false) {
-			global $ADODB_FETCH_MODE;
-			$mode = $ADODB_FETCH_MODE;
-		}
-		switch ($mode)
-		{
-		case ADODB_FETCH_NUM: $this->fetchMode = MYSQL_NUM; break;
-		case ADODB_FETCH_ASSOC:$this->fetchMode = MYSQL_ASSOC; break;
-		case ADODB_FETCH_DEFAULT:
-		case ADODB_FETCH_BOTH:
-		default:
-			$this->fetchMode = MYSQL_BOTH; break;
-		}
-		$this->adodbFetchMode = $mode;
-		parent::__construct($queryID);
-	}
-
-	function _initrs()
-	{
-	//GLOBAL $ADODB_COUNTRECS;
-	//	$this->_numOfRows = ($ADODB_COUNTRECS) ? @mysql_num_rows($this->_queryID):-1;
-		$this->_numOfRows = @mysql_num_rows($this->_queryID);
-		$this->_numOfFields = @mysql_num_fields($this->_queryID);
-	}
-
-	function fetchField($fieldOffset = -1)
-	{
-		if ($fieldOffset != -1) {
-			$o = @mysql_fetch_field($this->_queryID, $fieldOffset);
-			$f = @mysql_field_flags($this->_queryID,$fieldOffset);
-			if ($o) $o->max_length = @mysql_field_len($this->_queryID,$fieldOffset); // suggested by: Jim Nicholson (jnich#att.com)
-			//$o->max_length = -1; // mysql returns the max length less spaces -- so it is unrealiable
-			if ($o) $o->binary = (strpos($f,'binary')!== false);
-		}
-		else {	/*	The $fieldOffset argument is not provided thus its -1 	*/
-			$o = @mysql_fetch_field($this->_queryID);
-			//if ($o) $o->max_length = @mysql_field_len($this->_queryID); // suggested by: Jim Nicholson (jnich#att.com)
-			$o->max_length = -1; // mysql returns the max length less spaces -- so it is unrealiable
-		}
-
-		return $o;
-	}
-
-	function getRowAssoc($upper = ADODB_ASSOC_CASE)
-	{
-		if ($this->fetchMode == MYSQL_ASSOC && $upper == ADODB_ASSOC_CASE_LOWER) {
-			$row = $this->fields;
-		}
-		else {
-			$row = ADORecordSet::GetRowAssoc($upper);
-		}
-		return $row;
-	}
-
-	/* Use associative array to get fields array */
-	function fields($colname)
-	{
-		// added @ by "Michael William Miller" 
-		if ($this->fetchMode != MYSQL_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)]];
-	}
-
-	function _seek($row)
-	{
-		if ($this->_numOfRows == 0) return false;
-		return @mysql_data_seek($this->_queryID,$row);
-	}
-
-	function moveNext()
-	{
-		if (@$this->fields = mysql_fetch_array($this->_queryID,$this->fetchMode)) {
-			$this->_updatefields();
-			$this->_currentRow += 1;
-			return true;
-		}
-		if (!$this->EOF) {
-			$this->_currentRow += 1;
-			$this->EOF = true;
-		}
-		return false;
-	}
-
-	function _fetch()
-	{
-		$this->fields = @mysql_fetch_array($this->_queryID,$this->fetchMode);
-		$this->_updatefields();
-		return is_array($this->fields);
-	}
-
-	function _close() {
-		@mysql_free_result($this->_queryID);
-		$this->_queryID = false;
-	}
-
-	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 'STRING':
-		case 'CHAR':
-		case 'VARCHAR':
-		case 'TINYBLOB':
-		case 'TINYTEXT':
-		case 'ENUM':
-		case 'SET':
-			if ($len <= $this->blobSize) return 'C';
-
-		case 'TEXT':
-		case 'LONGTEXT':
-		case 'MEDIUMTEXT':
-			return 'X';
-
-		// php_mysql extension always returns 'blob' even if 'text'
-		// so we have to check whether binary...
-		case 'IMAGE':
-		case 'LONGBLOB':
-		case 'BLOB':
-		case 'MEDIUMBLOB':
-		case 'BINARY':
-			return !empty($fieldobj->binary) ? 'B' : 'X';
-
-		case 'YEAR':
-		case 'DATE': return 'D';
-
-		case 'TIME':
-		case 'DATETIME':
-		case 'TIMESTAMP': return 'T';
-
-		case 'INT':
-		case 'INTEGER':
-		case 'BIGINT':
-		case 'TINYINT':
-		case 'MEDIUMINT':
-		case 'SMALLINT':
-
-			if (!empty($fieldobj->primary_key)) return 'R';
-			else return 'I';
-
-		default: return ADODB_DEFAULT_METATYPE;
-		}
-	}
-
-}
-
-/**
- * Class ADORecordSet_ext_mysql
- */
-class ADORecordSet_ext_mysql extends ADORecordSet_mysql {
-
-	function moveNext()
-	{
-		return @adodb_movenext($this);
-	}
-}
-
-}
diff --git a/lib/adodb/drivers/adodb-mysqli.inc.php b/lib/adodb/drivers/adodb-mysqli.inc.php
index 160f5fa3406..0100e7f832b 100644
--- a/lib/adodb/drivers/adodb-mysqli.inc.php
+++ b/lib/adodb/drivers/adodb-mysqli.inc.php
@@ -75,12 +75,17 @@ class ADODB_mysqli extends ADOConnection {
 	var $ssl_capath = null;
 	var $ssl_cipher = null;
 
+	/** @var mysqli Identifier for the native database connection */
+	var $_connectionID = false;
+
 	/**
 	 * Tells the insert_id method how to obtain the last value, depending on whether
 	 * we are using a stored procedure or not
 	 */
 	private $usePreparedStatement = false;
 	private $useLastInsertStatement = false;
+	private $usingBoundVariables = false;
+	private $statementAffectedRows = -1;
 
 	/**
 	 * @var bool True if the last executed statement is a SELECT {@see _query()}
@@ -123,13 +128,12 @@ class ADODB_mysqli extends ADOConnection {
 	/**
 	 * Adds a parameter to the connection string.
 	 *
-	 * Parameter must be one of the the constants listed in mysqli_options().
+	 * Parameter must be one of the constants listed in mysqli_options().
 	 * @see https://www.php.net/manual/en/mysqli.options.php
 	 *
-	 * @param int $parameter The parameter to set
-	 * @param string $value The value of the parameter
+	 * @param int    $parameter The parameter to set
+	 * @param string $value     The value of the parameter
 	 *
-	 * @example, for mssqlnative driver ('CharacterSet','UTF-8')
 	 * @return bool
 	 */
 	public function setConnectionParameter($parameter, $value) {
@@ -137,8 +141,7 @@ class ADODB_mysqli extends ADOConnection {
 			$this->outp_throw("Invalid connection parameter '$parameter'", __METHOD__);
 			return false;
 		}
-		$this->connectionParameters[$parameter] = $value;
-		return true;
+		return parent::setConnectionParameter($parameter, $value);
 	}
 
 	/**
@@ -184,13 +187,15 @@ class ADODB_mysqli extends ADOConnection {
 		}
 
 		// Now merge in the standard connection parameters setting
-		foreach ($this->connectionParameters as $parameter => $value) {
-			// Make sure parameter is numeric before calling mysqli_options()
-			// that to avoid Warning (or TypeError exception on PHP 8).
-			if (!is_numeric($parameter)
-				|| !mysqli_options($this->_connectionID, $parameter, $value)
-			) {
-				$this->outp_throw("Invalid connection parameter '$parameter'", __METHOD__);
+		foreach ($this->connectionParameters as $options) {
+			foreach ($options as $parameter => $value) {
+				// Make sure parameter is numeric before calling mysqli_options()
+				// to avoid Warning (or TypeError exception on PHP 8).
+				if (!is_numeric($parameter)
+					|| !mysqli_options($this->_connectionID, $parameter, $value)
+				) {
+					$this->outp_throw("Invalid connection parameter '$parameter'", __METHOD__);
+				}
 			}
 		}
 
@@ -202,9 +207,12 @@ class ADODB_mysqli extends ADOConnection {
 		// SSL Connections for MySQLI
 		if ($this->ssl_key || $this->ssl_cert || $this->ssl_ca || $this->ssl_capath || $this->ssl_cipher) {
 			mysqli_ssl_set($this->_connectionID, $this->ssl_key, $this->ssl_cert, $this->ssl_ca, $this->ssl_capath, $this->ssl_cipher);
+			$this->socket = MYSQLI_CLIENT_SSL;
+			$this->clientFlags = MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT;
 		}
 
 		#if (!empty($this->port)) $argHostname .= ":".$this->port;
+		/** @noinspection PhpCastIsUnnecessaryInspection */
 		$ok = @mysqli_real_connect($this->_connectionID,
 					$argHostname,
 					$argUsername,
@@ -252,15 +260,15 @@ class ADODB_mysqli extends ADOConnection {
 	 * @param string|null $argHostname The host to connect to.
 	 * @param string|null $argUsername The username to connect as.
 	 * @param string|null $argPassword The password to connect with.
-	 * @param string|null $argDatabasename The name of the database to start in when connected.
+	 * @param string|null $argDatabaseName The name of the database to start in when connected.
 	 *
 	 * @return bool|null True if connected successfully, false if connection failed, or null if the mysqli extension
 	 * isn't currently loaded.
 	 */
-	function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
+	function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName)
 	{
 		$this->forceNewConnect = true;
-		return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename);
+		return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName);
 	}
 
 	/**
@@ -376,17 +384,17 @@ class ADODB_mysqli extends ADOConnection {
 	 *
 	 * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:rowlock
 	 *
-	 * @param string $tables The table(s) to lock rows for.
+	 * @param string $table The table(s) to lock rows for.
 	 * @param string $where (Optional) The WHERE clause to use to determine which rows to lock.
 	 * @param string $col (Optional) The columns to select.
 	 *
 	 * @return bool True if the locking SQL statement executed successfully, otherwise false.
 	 */
-	function RowLock($tables, $where = '', $col = '1 as adodbignore')
+	function RowLock($table, $where = '', $col = '1 as adodbignore')
 	{
 		if ($this->transCnt==0) $this->beginTrans();
 		if ($where) $where = ' where '.$where;
-		$rs = $this->execute("select $col from $tables $where for update");
+		$rs = $this->execute("select $col from $table $where for update");
 		return !empty($rs);
 	}
 
@@ -457,10 +465,17 @@ class ADODB_mysqli extends ADOConnection {
 			// the rowcount, but ADOdb does not do that.
 			return false;
 		}
-
-		$result =  @mysqli_affected_rows($this->_connectionID);
-		if ($result == -1) {
-			if ($this->debug) ADOConnection::outp("mysqli_affected_rows() failed : "  . $this->errorMsg());
+		else if ($this->statementAffectedRows >= 0)
+		{
+			$result = $this->statementAffectedRows;
+			$this->statementAffectedRows = -1;
+		}
+		else
+		{
+			$result =  @mysqli_affected_rows($this->_connectionID);
+			if ($result == -1) {
+				if ($this->debug) ADOConnection::outp("mysqli_affected_rows() failed : "  . $this->errorMsg());
+			}
 		}
 		return $result;
 	}
@@ -566,7 +581,6 @@ class ADODB_mysqli extends ADOConnection {
 		// save old fetch mode
 		global $ADODB_FETCH_MODE;
 
-		$false = false;
 		$save = $ADODB_FETCH_MODE;
 		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 		if ($this->fetchMode !== FALSE) {
@@ -574,7 +588,7 @@ class ADODB_mysqli extends ADOConnection {
 		}
 
 		// get index details
-		$rs = $this->execute(sprintf('SHOW INDEXES FROM %s',$table));
+		$rs = $this->Execute(sprintf('SHOW INDEXES FROM `%s`',$table));
 
 		// restore fetchmode
 		if (isset($savem)) {
@@ -583,7 +597,7 @@ class ADODB_mysqli extends ADOConnection {
 		$ADODB_FETCH_MODE = $save;
 
 		if (!is_object($rs)) {
-			return $false;
+			return false;
 		}
 
 		$indexes = array ();
@@ -621,7 +635,7 @@ class ADODB_mysqli extends ADOConnection {
 	 * @param string $fmt The date format to use.
 	 * @param string|bool $col (Optional) The table column to date format, or if false, use NOW().
 	 *
-	 * @return bool|string The SQL DATE_FORMAT() string, or false if the provided date format was empty.
+	 * @return string The SQL DATE_FORMAT() string, or false if the provided date format was empty.
 	 */
 	function SQLDate($fmt, $col = false)
 	{
@@ -740,13 +754,13 @@ class ADODB_mysqli extends ADOConnection {
 	/**
 	 * Returns information about stored procedures and stored functions.
 	 *
-	 * @param string|bool $NamePattern (Optional) Only look for procedures/functions with a name matching this pattern.
+	 * @param string|bool $procedureNamePattern (Optional) Only look for procedures/functions with a name matching this pattern.
 	 * @param null $catalog (Optional) Unused.
 	 * @param null $schemaPattern (Optional) Unused.
 	 *
 	 * @return array
 	 */
-	function MetaProcedures($NamePattern = false, $catalog  = null, $schemaPattern  = null)
+	function MetaProcedures($procedureNamePattern = false, $catalog  = null, $schemaPattern  = null)
 	{
 		// save old fetch mode
 		global $ADODB_FETCH_MODE;
@@ -763,8 +777,8 @@ class ADODB_mysqli extends ADOConnection {
 		// get index details
 
 		$likepattern = '';
-		if ($NamePattern) {
-			$likepattern = " LIKE '".$NamePattern."'";
+		if ($procedureNamePattern) {
+			$likepattern = " LIKE '".$procedureNamePattern."'";
 		}
 		$rs = $this->execute('SHOW PROCEDURE STATUS'.$likepattern);
 		if (is_object($rs)) {
@@ -840,9 +854,8 @@ class ADODB_mysqli extends ADOConnection {
 	 *
 	 * @return array|bool An array of foreign keys, or false no foreign keys could be found.
 	 */
-	function MetaForeignKeys($table, $owner = false, $upper = false, $associative = false)
+	public function metaForeignKeys($table, $owner = '', $upper = false, $associative = false)
 	{
-
 		global $ADODB_FETCH_MODE;
 
 		if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC
@@ -856,7 +869,7 @@ class ADODB_mysqli extends ADOConnection {
 			$table = "$owner.$table";
 		}
 
-		$a_create_table = $this->getRow(sprintf('SHOW CREATE TABLE %s', $table));
+		$a_create_table = $this->getRow(sprintf('SHOW CREATE TABLE `%s`', $table));
 
 		$this->setFetchMode($savem);
 
@@ -935,7 +948,6 @@ class ADODB_mysqli extends ADOConnection {
 		while (!$rs->EOF) {
 			$fld = new ADOFieldObject();
 			$fld->name = $rs->fields[0];
-			$type = $rs->fields[1];
 
 			/*
 			* Type from information_schema returns
@@ -1027,7 +1039,7 @@ class ADODB_mysqli extends ADOConnection {
 	 * @param int $nrows (Optional) The limit for the number of records you want returned. By default, all results.
 	 * @param int $offset (Optional) The offset to use when selecting the results. By default, no offset.
 	 * @param array|bool $inputarr (Optional) Any parameter values required by the SQL statement, or false if none.
-	 * @param int $secs (Optional) If greater than 0, perform a cached execute. By default, normal execution.
+	 * @param int $secs2cache (Optional) If greater than 0, perform a cached execute. By default, normal execution.
 	 *
 	 * @return ADORecordSet|false The query results, or false if the query failed to execute.
 	 */
@@ -1035,15 +1047,15 @@ class ADODB_mysqli extends ADOConnection {
 						 $nrows = -1,
 						 $offset = -1,
 						 $inputarr = false,
-						 $secs = 0)
+						 $secs2cache = 0)
 	{
 		$nrows = (int) $nrows;
 		$offset = (int) $offset;
 		$offsetStr = ($offset >= 0) ? "$offset," : '';
 		if ($nrows < 0) $nrows = '18446744073709551615';
 
-		if ($secs)
-			$rs = $this->cacheExecute($secs, $sql . " LIMIT $offsetStr$nrows" , $inputarr );
+		if ($secs2cache)
+			$rs = $this->cacheExecute($secs2cache, $sql . " LIMIT $offsetStr$nrows" , $inputarr );
 		else
 			$rs = $this->execute($sql . " LIMIT $offsetStr$nrows" , $inputarr );
 
@@ -1052,6 +1064,7 @@ class ADODB_mysqli extends ADOConnection {
 
 	/**
 	 * Prepares an SQL statement and returns a handle to use.
+	 * This is not used by bound parameters anymore
 	 *
 	 * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:prepare
 	 * @todo update this function to handle prepared statements correctly
@@ -1080,13 +1093,106 @@ class ADODB_mysqli extends ADOConnection {
 	}
 
 	/**
-	 * Return the query id.
+	 * Execute SQL
 	 *
-	 * @param string|array $sql
-	 * @param array $inputarr
+	 * @param string     $sql      SQL statement to execute, or possibly an array
+	 *                             holding prepared statement ($sql[0] will hold sql text)
+	 * @param array|bool $inputarr holds the input data to bind to.
+	 *                             Null elements will be set to null.
 	 *
-	 * @return bool|mysqli_result
+	 * @return ADORecordSet|bool
 	 */
+	public function execute($sql, $inputarr = false)
+	{
+		if ($this->fnExecute) {
+			$fn = $this->fnExecute;
+			$ret = $fn($this, $sql, $inputarr);
+			if (isset($ret)) {
+				return $ret;
+			}
+		}
+
+		if ($inputarr === false || $inputarr === []) {
+			return $this->_execute($sql);
+		}
+
+		if (!is_array($inputarr)) {
+			$inputarr = array($inputarr);
+		}
+
+		if (!is_array($sql)) {
+			// Check if we are bulkbinding. If so, $inputarr is a 2d array,
+			// and we make a gross assumption that all rows have the same number
+			// of columns of the same type, and use the elements of the first row
+			// to determine the MySQL bind param types.
+			if (is_array($inputarr[0])) {
+				if (!$this->bulkBind) {
+					$this->outp_throw(
+						"2D Array of values sent to execute and 'ADOdb_mysqli::bulkBind' not set",
+						'Execute'
+					);
+					return false;
+				}
+
+				$bulkTypeArray = [];
+				foreach ($inputarr as $v) {
+					if (is_string($this->bulkBind)) {
+						$typeArray = array_merge((array)$this->bulkBind, $v);
+					} else {
+						$typeArray = $this->getBindParamWithType($v);
+					}
+					$bulkTypeArray[] = $typeArray;
+				}
+				$this->bulkBind = false;
+				$ret = $this->_execute($sql, $bulkTypeArray);
+			} else {
+				$typeArray = $this->getBindParamWithType($inputarr);
+				$ret = $this->_execute($sql, $typeArray);
+			}
+		} else {
+			$ret = $this->_execute($sql, $inputarr);
+		}
+		return $ret;
+	}
+
+	/**
+	 * Inserts the bind param type string at the front of the parameter array.
+	 *
+	 * @see https://www.php.net/manual/en/mysqli-stmt.bind-param.php
+	 *
+	 * @param array $inputArr
+	 * @return array
+	 */
+	private function getBindParamWithType($inputArr): array
+	{
+		$typeString = '';
+		foreach ($inputArr as $v) {
+			if (is_integer($v) || is_bool($v)) {
+				$typeString .= 'i';
+			} elseif (is_float($v)) {
+				$typeString .= 'd';
+			} elseif (is_object($v)) {
+				// Assume a blob
+				$typeString .= 'b';
+			} else {
+				$typeString .= 's';
+			}
+		}
+
+		// Place the field type list at the front of the parameter array.
+		// This is the mysql specific format
+		array_unshift($inputArr, $typeString);
+		return $inputArr;
+	}
+
+	/**
+	* Return the query id.
+	*
+	* @param string|array $sql
+	* @param array $inputarr
+	*
+	* @return bool|mysqli_result
+	*/
 	function _query($sql, $inputarr)
 	{
 		global $ADODB_COUNTRECS;
@@ -1104,7 +1210,7 @@ class ADODB_mysqli extends ADOConnection {
 
 			$stmt = $sql[1];
 			$a = '';
-			foreach($inputarr as $k => $v) {
+			foreach($inputarr as $v) {
 				if (is_string($v)) $a .= 's';
 				else if (is_integer($v)) $a .= 'i';
 				else $a .= 'd';
@@ -1118,8 +1224,105 @@ class ADODB_mysqli extends ADOConnection {
 
 			$fnarr = array_merge( array($stmt,$a) , $inputarr);
 			call_user_func_array('mysqli_stmt_bind_param',$fnarr);
-			$ret = mysqli_stmt_execute($stmt);
-			return $ret;
+			return mysqli_stmt_execute($stmt);
+		}
+		else if (is_string($sql) && is_array($inputarr))
+		{
+
+			/*
+			* This is support for true prepared queries
+			* with bound parameters
+			*
+			* set prepared statement flags
+			*/
+			$this->usePreparedStatement = true;
+			$this->usingBoundVariables = true;
+
+			$bulkBindArray = array();
+			if (is_array($inputarr[0]))
+			{
+				$bulkBindArray = $inputarr;
+				$inputArrayCount = count($inputarr[0]) - 1;
+			}
+			else
+			{
+				$bulkBindArray[] = $inputarr;
+				$inputArrayCount = count($inputarr) - 1;
+			}
+
+
+			/*
+			* Prepare the statement with the placeholders,
+			* prepare will fail if the statement is invalid
+			* so we trap and error if necessary. Note that we
+			* are calling MySQL prepare here, not ADOdb
+			*/
+			$stmt = $this->_connectionID->prepare($sql);
+			if ($stmt === false)
+			{
+				$this->outp_throw(
+					"SQL Statement failed on preparation: " . htmlspecialchars($sql) . "'",
+					'Execute'
+				);
+				return false;
+			}
+			/*
+			* Make sure the number of parameters provided in the input
+			* array matches what the query expects. We must discount
+			* the first parameter which contains the data types in
+			* our inbound parameters
+			*/
+			$nparams = $stmt->param_count;
+
+			if ($nparams  != $inputArrayCount)
+			{
+
+				$this->outp_throw(
+					"Input array has " . $inputArrayCount .
+					" params, does not match query: '" . htmlspecialchars($sql) . "'",
+					'Execute'
+				);
+				return false;
+			}
+
+			foreach ($bulkBindArray as $inputarr)
+			{
+				/*
+				* Must pass references into call_user_func_array
+				*/
+				$paramsByReference = array();
+				foreach($inputarr as $key => $value) {
+					/** @noinspection PhpArrayAccessCanBeReplacedWithForeachValueInspection */
+					$paramsByReference[$key] = &$inputarr[$key];
+				}
+
+				/*
+				* Bind the params
+				*/
+				call_user_func_array(array($stmt, 'bind_param'), $paramsByReference);
+
+				/*
+				* Execute
+				*/
+
+				$ret = mysqli_stmt_execute($stmt);
+
+				/*
+				* Did we throw an error?
+				*/
+				if ($ret == false)
+					return false;
+			}
+
+			// Tells affected_rows to be compliant
+			$this->isSelectStatement = $stmt->affected_rows == -1;
+			if (!$this->isSelectStatement) {
+				$this->statementAffectedRows = $stmt->affected_rows;
+				return true;
+			}
+
+			// Turn the statement into a result set and return it
+			return $stmt->get_result();
 		}
 		else
 		{
@@ -1144,7 +1347,7 @@ class ADODB_mysqli extends ADOConnection {
 			$rs = mysqli_multi_query($this->_connectionID, $sql.';');
 			if ($rs) {
 				$rs = ($ADODB_COUNTRECS) ? @mysqli_store_result( $this->_connectionID ) : @mysqli_use_result( $this->_connectionID );
-				return $rs ? $rs : true; // mysqli_more_results( $this->_connectionID )
+				return $rs ?: true; // mysqli_more_results( $this->_connectionID )
 			}
 		} else {
 			$rs = mysqli_query($this->_connectionID, $sql, $ADODB_COUNTRECS ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT);
@@ -1262,6 +1465,12 @@ class ADORecordSet_mysqli extends ADORecordSet{
 	var $databaseType = "mysqli";
 	var $canSeek = true;
 
+	/** @var ADODB_mysqli The parent connection */
+	var $connection = false;
+
+	/** @var mysqli_result result link identifier */
+	var $_queryID;
+
 	function __construct($queryID, $mode = false)
 	{
 		if ($mode === false) {
@@ -1368,8 +1577,7 @@ class ADORecordSet_mysqli extends ADORecordSet{
 		if ($this->fetchMode == MYSQLI_ASSOC && $upper == ADODB_ASSOC_CASE_LOWER) {
 			return $this->fields;
 		}
-		$row = ADORecordSet::getRowAssoc($upper);
-		return $row;
+		return ADORecordSet::getRowAssoc($upper);
 	}
 
 	/**
@@ -1524,6 +1732,7 @@ class ADORecordSet_mysqli extends ADORecordSet{
 12 = MYSQLI_TYPE_DATETIME
 13 = MYSQLI_TYPE_YEAR
 14 = MYSQLI_TYPE_NEWDATE
+245 = MYSQLI_TYPE_JSON
 247 = MYSQLI_TYPE_ENUM
 248 = MYSQLI_TYPE_SET
 249 = MYSQLI_TYPE_TINY_BLOB
@@ -1544,7 +1753,7 @@ class ADORecordSet_mysqli extends ADORecordSet{
 	 *
 	 * @return string The MetaType
 	 */
-	function MetaType($t, $len = -1, $fieldobj = false)
+	function metaType($t, $len = -1, $fieldobj = false)
 	{
 		if (is_object($t)) {
 			$fieldobj = $t;
@@ -1552,8 +1761,16 @@ class ADORecordSet_mysqli extends ADORecordSet{
 			$len = $fieldobj->max_length;
 		}
 
+		$t = strtoupper($t);
+		/*
+		* Add support for custom actual types. We do this
+		* first, that allows us to override existing types
+		*/
+		if (array_key_exists($t,$this->connection->customActualTypes))
+			return  $this->connection->customActualTypes[$t];
+
 		$len = -1; // mysql max_length is not accurate
-		switch (strtoupper($t)) {
+		switch ($t) {
 			case 'STRING':
 			case 'CHAR':
 			case 'VARCHAR':
@@ -1631,6 +1848,8 @@ class ADORecordSet_mysqli extends ADORecordSet{
 			case 'DEC':
 			case 'FIXED':
 			default:
+
+
 				//if (!is_numeric($t)) echo "

--- Error in type matching $t -----

"; return 'N'; } @@ -1661,8 +1880,14 @@ class ADORecordSet_array_mysqli extends ADORecordSet_array $len = $fieldobj->max_length; } + $t = strtoupper($t); + + if (array_key_exists($t,$this->connection->customActualTypes)) + return $this->connection->customActualTypes[$t]; + $len = -1; // mysql max_length is not accurate - switch (strtoupper($t)) { + + switch ($t) { case 'STRING': case 'CHAR': case 'VARCHAR': diff --git a/lib/adodb/drivers/adodb-mysqlpo.inc.php b/lib/adodb/drivers/adodb-mysqlpo.inc.php deleted file mode 100644 index 8aa7779ad46..00000000000 --- a/lib/adodb/drivers/adodb-mysqlpo.inc.php +++ /dev/null @@ -1,124 +0,0 @@ -transOff) return true; - $this->transCnt += 1; - $this->Execute('SET AUTOCOMMIT=0'); - $this->Execute('BEGIN'); - return true; - } - - function CommitTrans($ok=true) - { - if ($this->transOff) return true; - if (!$ok) return $this->RollbackTrans(); - - if ($this->transCnt) $this->transCnt -= 1; - $this->Execute('COMMIT'); - $this->Execute('SET AUTOCOMMIT=1'); - return true; - } - - function RollbackTrans() - { - if ($this->transOff) return true; - if ($this->transCnt) $this->transCnt -= 1; - $this->Execute('ROLLBACK'); - $this->Execute('SET AUTOCOMMIT=1'); - return true; - } - - function RowLock($tables,$where='',$col='1 as adodbignore') - { - if ($this->transCnt==0) $this->BeginTrans(); - if ($where) $where = ' where '.$where; - $rs = $this->Execute("select $col from $tables $where for update"); - return !empty($rs); - } - -} - -class ADORecordSet_mysqlt extends ADORecordSet_mysql{ - var $databaseType = "mysqlt"; - - function __construct($queryID,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - - switch ($mode) - { - case ADODB_FETCH_NUM: $this->fetchMode = MYSQL_NUM; break; - case ADODB_FETCH_ASSOC:$this->fetchMode = MYSQL_ASSOC; break; - - case ADODB_FETCH_DEFAULT: - case ADODB_FETCH_BOTH: - default: $this->fetchMode = MYSQL_BOTH; break; - } - - $this->adodbFetchMode = $mode; - parent::__construct($queryID); - } - - function MoveNext() - { - if (@$this->fields = mysql_fetch_array($this->_queryID,$this->fetchMode)) { - $this->_currentRow += 1; - return true; - } - if (!$this->EOF) { - $this->_currentRow += 1; - $this->EOF = true; - } - return false; - } -} - -class ADORecordSet_ext_mysqlt extends ADORecordSet_mysqlt { - - function MoveNext() - { - return adodb_movenext($this); - } -} diff --git a/lib/adodb/drivers/adodb-mysqlt.inc.php b/lib/adodb/drivers/adodb-mysqlt.inc.php deleted file mode 100644 index cccd93f8d34..00000000000 --- a/lib/adodb/drivers/adodb-mysqlt.inc.php +++ /dev/null @@ -1,141 +0,0 @@ -_transmode = $transaction_mode; - if (empty($transaction_mode)) { - $this->Execute('SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ'); - return; - } - if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode; - $this->Execute("SET SESSION TRANSACTION ".$transaction_mode); - } - - function BeginTrans() - { - if ($this->transOff) return true; - $this->transCnt += 1; - $this->Execute('SET AUTOCOMMIT=0'); - $this->Execute('BEGIN'); - return true; - } - - function CommitTrans($ok=true) - { - if ($this->transOff) return true; - if (!$ok) return $this->RollbackTrans(); - - if ($this->transCnt) $this->transCnt -= 1; - $ok = $this->Execute('COMMIT'); - $this->Execute('SET AUTOCOMMIT=1'); - return $ok ? true : false; - } - - function RollbackTrans() - { - if ($this->transOff) return true; - if ($this->transCnt) $this->transCnt -= 1; - $ok = $this->Execute('ROLLBACK'); - $this->Execute('SET AUTOCOMMIT=1'); - return $ok ? true : false; - } - - function RowLock($tables,$where='',$col='1 as adodbignore') - { - if ($this->transCnt==0) $this->BeginTrans(); - if ($where) $where = ' where '.$where; - $rs = $this->Execute("select $col from $tables $where for update"); - return !empty($rs); - } - -} - -class ADORecordSet_mysqlt extends ADORecordSet_mysql{ - var $databaseType = "mysqlt"; - - function __construct($queryID,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - - switch ($mode) - { - case ADODB_FETCH_NUM: $this->fetchMode = MYSQL_NUM; break; - case ADODB_FETCH_ASSOC:$this->fetchMode = MYSQL_ASSOC; break; - - case ADODB_FETCH_DEFAULT: - case ADODB_FETCH_BOTH: - default: $this->fetchMode = MYSQL_BOTH; break; - } - - $this->adodbFetchMode = $mode; - parent::__construct($queryID); - } - - function MoveNext() - { - if (@$this->fields = mysql_fetch_array($this->_queryID,$this->fetchMode)) { - $this->_currentRow += 1; - return true; - } - if (!$this->EOF) { - $this->_currentRow += 1; - $this->EOF = true; - } - return false; - } -} - -class ADORecordSet_ext_mysqlt extends ADORecordSet_mysqlt { - - function MoveNext() - { - return adodb_movenext($this); - } -} diff --git a/lib/adodb/drivers/adodb-oci8.inc.php b/lib/adodb/drivers/adodb-oci8.inc.php index e541b6b6a0a..c1f4e502053 100644 --- a/lib/adodb/drivers/adodb-oci8.inc.php +++ b/lib/adodb/drivers/adodb-oci8.inc.php @@ -791,6 +791,11 @@ END; $nrows += $offset; } $sql = "select * from (".$sql.") where rownum <= :adodb_offset"; + + // If non-bound statement, $inputarr is false + if (!$inputarr) { + $inputarr = array(); + } $inputarr['adodb_offset'] = $nrows; $nrows = -1; } @@ -973,9 +978,12 @@ END; /** * 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 + * @param string|array $sql SQL statement to execute, or possibly an array holding + * prepared statement ($sql[0] will hold sql text). + * @param array|false $inputarr holds the input data to bind to. + * Null elements will be set to null. + * + * @return ADORecordSet|false */ function Execute($sql,$inputarr=false) { @@ -1094,6 +1102,22 @@ END; return array($sql,$stmt,0,$BINDNUM); } + function releaseStatement(&$stmt) + { + if (is_array($stmt) + && isset($stmt[1]) + && is_resource($stmt[1]) + && oci_free_statement($stmt[1]) + ) { + // Clearing the resource to avoid it being of type Unknown + $stmt[1] = null; + return true; + } + + // Not a valid prepared statement + return false; + } + /* Call an oracle stored procedure and returns a cursor variable as a recordset. Concept by Robert Tuttle robert@ud.com @@ -1241,12 +1265,12 @@ END; * $db->Parameter($stmt,$group,'group'); * $db->Execute($stmt); * - * @param $stmt Statement returned by Prepare() or PrepareSP(). + * @param $stmt Statement returned by {@see Prepare()} or {@see 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. + * @param bool $isOutput Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8. + * @param int $maxLen Holds an maximum length of the variable. + * @param mixed $type The data type of $var. Legal values depend on driver. * * @link http://php.net/oci_bind_by_name */ @@ -1484,16 +1508,17 @@ SELECT /*+ RULE */ distinct b.column_name } /** - * returns assoc array where keys are tables, and values are foreign keys + * Returns a list of Foreign Keys associated with a specific table. * - * @param str $table - * @param str $owner [optional][default=NULL] - * @param bool $upper [optional][discarded] - * @return mixed[] Array of foreign key information + * @param string $table + * @param string $owner + * @param bool $upper discarded + * @param bool $associative discarded * - * @link http://gis.mit.edu/classes/11.521/sqlnotes/referential_integrity.html + * @return string[]|false An array where keys are tables, and values are foreign keys; + * false if no foreign keys could be found. */ - function MetaForeignKeys($table, $owner=false, $upper=false) + public function metaForeignKeys($table, $owner = '', $upper = false, $associative = false) { global $ADODB_FETCH_MODE; @@ -1810,7 +1835,12 @@ class ADORecordset_oci8 extends ADORecordSet { $len = $fieldobj->max_length; } - switch (strtoupper($t)) { + $t = strtoupper($t); + + if (array_key_exists($t,$this->connection->customActualTypes)) + return $this->connection->customActualTypes[$t]; + + switch ($t) { case 'VARCHAR': case 'VARCHAR2': case 'CHAR': diff --git a/lib/adodb/drivers/adodb-odbc_mssql.inc.php b/lib/adodb/drivers/adodb-odbc_mssql.inc.php index 37185aac32d..9f53d3d7c3d 100644 --- a/lib/adodb/drivers/adodb-odbc_mssql.inc.php +++ b/lib/adodb/drivers/adodb-odbc_mssql.inc.php @@ -81,8 +81,7 @@ class ADODB_odbc_mssql extends ADODB_odbc { return $this->GetOne($this->identitySQL); } - - function MetaForeignKeys($table, $owner=false, $upper=false) + public function metaForeignKeys($table, $owner = '', $upper = false, $associative = false) { global $ADODB_FETCH_MODE; diff --git a/lib/adodb/drivers/adodb-odbtp.inc.php b/lib/adodb/drivers/adodb-odbtp.inc.php index bd167a1c8c8..41bd0873bac 100644 --- a/lib/adodb/drivers/adodb-odbtp.inc.php +++ b/lib/adodb/drivers/adodb-odbtp.inc.php @@ -393,7 +393,7 @@ class ADODB_odbtp extends ADOConnection{ return $arr2; } - function MetaForeignKeys($table, $owner='', $upper=false) + public function metaForeignKeys($table, $owner = '', $upper = false, $associative = false) { global $ADODB_FETCH_MODE; diff --git a/lib/adodb/drivers/adodb-pdo.inc.php b/lib/adodb/drivers/adodb-pdo.inc.php index cf8e4e2644b..e0e6c729724 100644 --- a/lib/adodb/drivers/adodb-pdo.inc.php +++ b/lib/adodb/drivers/adodb-pdo.inc.php @@ -82,7 +82,6 @@ class ADODB_pdo extends ADOConnection { var $_errormsg = false; var $_errorno = false; - var $dsnType = ''; var $stmt = false; var $_driver; @@ -238,6 +237,26 @@ class ADODB_pdo extends ADOConnection { return call_user_func_array('parent::Concat', $args); } + /** + * Triggers a driver-specific request for a bind parameter + * + * @param string $name + * @param string $type + * + * @return string + */ + public function param($name,$type='C') { + + $args = func_get_args(); + if(method_exists($this->_driver, 'param')) { + // Return the driver specific entry, that mimics the native driver + return call_user_func_array(array($this->_driver, 'param'), $args); + } + + // No driver specific method defined, use mysql format '?' + return call_user_func_array('parent::param', $args); + } + // returns true or false function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename) { @@ -294,18 +313,19 @@ class ADODB_pdo extends ADOConnection { } /** - * Returns a list of Foreign Keys for a specified table. + * Returns a list of Foreign Keys associated with a specific table. * * @param string $table - * @param bool $owner (optional) not used in this driver + * @param string $owner (optional) not used in this driver * @param bool $upper * @param bool $associative * - * @return string[] where keys are tables, and values are foreign keys + * @return string[]|false An array where keys are tables, and values are foreign keys; + * false if no foreign keys could be found. */ - public function metaForeignKeys($table, $owner=false, $upper=false,$associative=false) { + public function metaForeignKeys($table, $owner = '', $upper = false, $associative = false) { if (method_exists($this->_driver,'metaForeignKeys')) - return $this->_driver->metaForeignKeys($table,$owner,$upper,$associative); + return $this->_driver->metaForeignKeys($table, $owner, $upper, $associative); } /** @@ -565,6 +585,7 @@ class ADODB_pdo extends ADOConnection { $this->_driver->debug = $this->debug; } if ($inputarr) { + /* * inputarr must be numeric */ diff --git a/lib/adodb/drivers/adodb-pdo_oci.inc.php b/lib/adodb/drivers/adodb-pdo_oci.inc.php index 9a05ee6a9cb..20b47deffe1 100644 --- a/lib/adodb/drivers/adodb-pdo_oci.inc.php +++ b/lib/adodb/drivers/adodb-pdo_oci.inc.php @@ -75,15 +75,15 @@ class ADODB_pdo_oci extends ADODB_pdo_base { $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->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' && $rs->fields[3] == 0) { $fld->type ='INT'; - $fld->max_length = $rs->fields[4]; - } - $fld->not_null = (strncmp($rs->fields[5], 'NOT',3) === 0); + $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]; @@ -98,12 +98,25 @@ class ADODB_pdo_oci extends ADODB_pdo_base { return $retarr; } - /** - * @param bool $auto_commit - * @return void - */ - function SetAutoCommit($auto_commit) - { - $this->_connectionID->setAttribute(PDO::ATTR_AUTOCOMMIT, $auto_commit); - } + /** + * @param bool $auto_commit + * @return void + */ + function SetAutoCommit($auto_commit) + { + $this->_connectionID->setAttribute(PDO::ATTR_AUTOCOMMIT, $auto_commit); + } + + /** + * Returns a driver-specific format for a bind parameter + * + * @param string $name + * @param string $type (ignored in driver) + * + * @return string + */ + public function param($name,$type='C') + { + return sprintf(':%s', $name); + } } diff --git a/lib/adodb/drivers/adodb-pdo_pgsql.inc.php b/lib/adodb/drivers/adodb-pdo_pgsql.inc.php index 0212d4fda64..6c06ac10319 100644 --- a/lib/adodb/drivers/adodb-pdo_pgsql.inc.php +++ b/lib/adodb/drivers/adodb-pdo_pgsql.inc.php @@ -21,11 +21,11 @@ class ADODB_pdo_pgsql extends ADODB_pdo { var $metaDatabasesSQL = "select datname from pg_database where datname not in ('template0','template1') order by 1"; - var $metaTablesSQL = "select tablename,'T' from pg_tables where tablename not like 'pg\_%' - and tablename not in ('sql_features', 'sql_implementation_info', 'sql_languages', - 'sql_packages', 'sql_sizing', 'sql_sizing_profiles') - union - select viewname,'V' from pg_views where viewname not like 'pg\_%'"; + var $metaTablesSQL = "select tablename,'T' from pg_tables where tablename not like 'pg\_%' + and tablename not in ('sql_features', 'sql_implementation_info', 'sql_languages', + 'sql_packages', 'sql_sizing', 'sql_sizing_profiles') + union + select viewname,'V' from pg_views where viewname not like 'pg\_%'"; //"select tablename from pg_tables where tablename not like 'pg_%' order by 1"; var $isoDates = true; // accepts dates in ISO format var $sysDate = "CURRENT_DATE"; @@ -34,15 +34,15 @@ class ADODB_pdo_pgsql extends ADODB_pdo { var $metaColumnsSQL = "SELECT a.attname,t.typname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,a.attnum FROM pg_class c, pg_attribute a,pg_type t WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s')) and a.attname not like '....%%' -AND a.attnum > 0 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum"; + AND a.attnum > 0 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum"; // used when schema defined var $metaColumnsSQL1 = "SELECT a.attname, t.typname, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum -FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n -WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s')) - and c.relnamespace=n.oid and n.nspname='%s' - and a.attname not like '....%%' AND a.attnum > 0 - AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum"; + FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n + WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s')) + and c.relnamespace=n.oid and n.nspname='%s' + and a.attname not like '....%%' AND a.attnum > 0 + AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum"; // get primary key etc -- from Freek Dijkstra var $metaKeySQL = "SELECT ic.relname AS index_name, a.attname AS column_name,i.indisunique AS unique_key, i.indisprimary AS primary_key @@ -97,23 +97,27 @@ WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s')) { $info = $this->ServerInfo(); if ($info['version'] >= 7.3) { - $this->metaTablesSQL = "select tablename,'T' from pg_tables where tablename not like 'pg\_%' - and schemaname not in ( 'pg_catalog','information_schema') - union - select viewname,'V' from pg_views where viewname not like 'pg\_%' and schemaname not in ( 'pg_catalog','information_schema') "; + $this->metaTablesSQL = " +select tablename,'T' from pg_tables +where tablename not like 'pg\_%' and schemaname not in ( 'pg_catalog','information_schema') +union +select viewname,'V' from pg_views +where viewname not like 'pg\_%' and schemaname not in ( 'pg_catalog','information_schema')"; } if ($mask) { $save = $this->metaTablesSQL; $mask = $this->qstr(strtolower($mask)); if ($info['version']>=7.3) $this->metaTablesSQL = " -select tablename,'T' from pg_tables where tablename like $mask and schemaname not in ( 'pg_catalog','information_schema') - union -select viewname,'V' from pg_views where viewname like $mask and schemaname not in ( 'pg_catalog','information_schema') "; +select tablename,'T' from pg_tables +where tablename like $mask and schemaname not in ( 'pg_catalog','information_schema') +union +select viewname,'V' from pg_views +where viewname like $mask and schemaname not in ( 'pg_catalog','information_schema')"; else $this->metaTablesSQL = " select tablename,'T' from pg_tables where tablename like $mask - union +union select viewname,'V' from pg_views where viewname like $mask"; } $ret = ADOConnection::MetaTables($ttype,$showSchema); @@ -297,4 +301,23 @@ select viewname,'V' from pg_views where viewname like $mask"; if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode; $this->_connectionID->query("SET TRANSACTION ".$transaction_mode); } + + /** + * Returns a driver-specific format for a bind parameter + * + * Unlike the native driver, we use :name parameters + * instead of offsets + * + * @param string $name + * @param string $type (ignored in driver) + * + * @return string + */ + public function param($name,$type='C') { + if (!$name) { + return ''; + } + + return sprintf(':%s', $name); + } } diff --git a/lib/adodb/drivers/adodb-pdo_sqlite.inc.php b/lib/adodb/drivers/adodb-pdo_sqlite.inc.php index b62ca35ed96..dab1de993fc 100644 --- a/lib/adodb/drivers/adodb-pdo_sqlite.inc.php +++ b/lib/adodb/drivers/adodb-pdo_sqlite.inc.php @@ -34,7 +34,7 @@ class ADODB_pdo_sqlite extends ADODB_pdo { var $_genSeq2SQL = 'INSERT INTO %s VALUES(%s)'; var $_dropSeqSQL = 'DROP TABLE %s'; var $concat_operator = '||'; - var $pdoDriver = false; + var $pdoDriver = false; var $random='abs(random())'; function _init($parentDriver) @@ -156,40 +156,48 @@ class ADODB_pdo_sqlite extends ADODB_pdo { // mark newnham function MetaColumns($tab,$normalize=true) { - global $ADODB_FETCH_MODE; + 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; + $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) @@ -208,5 +216,18 @@ class ADODB_pdo_sqlite extends ADODB_pdo { $this->metaTablesSQL = $save; } return $ret; - } + } + + /** + * Returns a driver-specific format for a bind parameter + * + * @param string $name + * @param string $type (ignored in driver) + * + * @return string + */ + public function param($name,$type='C') + { + return sprintf(':%s', $name); + } } diff --git a/lib/adodb/drivers/adodb-postgres64.inc.php b/lib/adodb/drivers/adodb-postgres64.inc.php index 3f351f97463..290967e7b57 100644 --- a/lib/adodb/drivers/adodb-postgres64.inc.php +++ b/lib/adodb/drivers/adodb-postgres64.inc.php @@ -26,6 +26,7 @@ class ADODB_postgres64 extends ADOConnection{ var $databaseType = 'postgres64'; var $dataProvider = 'postgres'; var $hasInsertID = true; + /** @var PgSql\Connection|resource|false */ var $_resultid = false; var $concat_operator='||'; var $metaDatabasesSQL = "select datname from pg_database where datname not in ('template0','template1') order by 1"; @@ -154,7 +155,7 @@ class ADODB_postgres64 extends ADOConnection{ */ protected function _insertID($table = '', $column = '') { - if (!is_resource($this->_resultid) || get_resource_type($this->_resultid) !== 'pgsql result') return false; + if ($this->_resultid === false) return false; $oid = pg_last_oid($this->_resultid); // to really return the id, we need the table and column-name, else we can only return the oid != id return empty($table) || empty($column) ? $oid : $this->GetOne("SELECT $column FROM $table WHERE oid=".(int)$oid); @@ -162,13 +163,13 @@ class ADODB_postgres64 extends ADOConnection{ function _affectedrows() { - if (!is_resource($this->_resultid) || get_resource_type($this->_resultid) !== 'pgsql result') return false; + if ($this->_resultid === false) return false; return pg_affected_rows($this->_resultid); } /** - * @return true/false + * @return bool */ function BeginTrans() { @@ -378,7 +379,7 @@ class ADODB_postgres64 extends ADOConnection{ function BlobDelete( $blob ) { pg_query($this->_connectionID, 'begin'); - $result = @pg_lo_unlink($blob); + $result = @pg_lo_unlink($this->_connectionID, $blob); pg_query($this->_connectionID, 'commit'); return( $result ); } @@ -808,8 +809,7 @@ class ADODB_postgres64 extends ADOConnection{ if ($execp) $exsql = "EXECUTE $plan ($execp)"; else $exsql = "EXECUTE $plan"; - - $rez = @pg_execute($this->_connectionID,$exsql); + $rez = @pg_query($this->_connectionID, $exsql); if (!$rez) { # Perhaps plan does not exist? Prepare/compile plan. $params = ''; @@ -833,18 +833,18 @@ class ADODB_postgres64 extends ADOConnection{ } $s = "PREPARE $plan ($params) AS ".substr($sql,0,strlen($sql)-2); //adodb_pr($s); - $rez = pg_execute($this->_connectionID,$s); + $rez = pg_query($this->_connectionID, $s); //echo $this->ErrorMsg(); } if ($rez) - $rez = pg_execute($this->_connectionID,$exsql); + $rez = pg_query($this->_connectionID, $exsql); } else { //adodb_backtrace(); - $rez = pg_query($this->_connectionID,$sql); + $rez = pg_query($this->_connectionID, $sql); } // check if no data returned, then no need to create real recordset if ($rez && pg_num_fields($rez) <= 0) { - if (is_resource($this->_resultid) && get_resource_type($this->_resultid) === 'pgsql result') { + if ($this->_resultid !== false) { pg_free_result($this->_resultid); } $this->_resultid = $rez; @@ -1020,8 +1020,20 @@ class ADORecordSet_postgres64 extends ADORecordSet{ return pg_unescape_bytea($blob); } - function _fixblobs() + /** + * Fetches and prepares the RecordSet's fields. + * + * Fixes the blobs if there are any. + */ + protected function _prepFields() { + $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); + + // Check prerequisites and bail early if we do not have what we need. + if (!isset($this->_blobArr) || $this->fields === false) { + return; + } + if ($this->fetchMode == PGSQL_NUM || $this->fetchMode == PGSQL_BOTH) { foreach($this->_blobArr as $k => $v) { $this->fields[$k] = ADORecordSet_postgres64::_decode($this->fields[$k]); @@ -1040,9 +1052,8 @@ class ADORecordSet_postgres64 extends ADORecordSet{ if (!$this->EOF) { $this->_currentRow++; if ($this->_numOfRows < 0 || $this->_numOfRows > $this->_currentRow) { - $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); - if (is_array($this->fields) && $this->fields) { - if (isset($this->_blobArr)) $this->_fixblobs(); + $this->_prepfields(); + if ($this->fields !== false) { return true; } } @@ -1054,22 +1065,17 @@ class ADORecordSet_postgres64 extends ADORecordSet{ function _fetch() { - - if ($this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0) + if ($this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0) { return false; + } - $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); - - if ($this->fields && isset($this->_blobArr)) $this->_fixblobs(); - - return (is_array($this->fields)); + $this->_prepfields(); + return $this->fields !== false; } function _close() { - if (!is_resource($this->_queryID) - || get_resource_type($this->_queryID) != 'pgsql result' - ) { + if ($this->_queryID === false) { return true; } return pg_free_result($this->_queryID); @@ -1082,7 +1088,13 @@ class ADORecordSet_postgres64 extends ADORecordSet{ $t = $fieldobj->type; $len = $fieldobj->max_length; } - switch (strtoupper($t)) { + + $t = strtoupper($t); + + if (array_key_exists($t,$this->connection->customActualTypes)) + return $this->connection->customActualTypes[$t]; + + switch ($t) { case 'MONEY': // stupid, postgres expects money to be a string case 'INTERVAL': case 'CHAR': @@ -1094,6 +1106,7 @@ class ADORecordSet_postgres64 extends ADORecordSet{ case 'CIDR': case 'INET': case 'MACADDR': + case 'UUID': if ($len <= $this->blobSize) return 'C'; case 'TEXT': @@ -1134,6 +1147,12 @@ class ADORecordSet_postgres64 extends ADORecordSet{ case 'SERIAL': return 'R'; + case 'NUMERIC': + case 'DECIMAL': + case 'FLOAT4': + case 'FLOAT8': + return 'N'; + default: return ADODB_DEFAULT_METATYPE; } diff --git a/lib/adodb/drivers/adodb-postgres7.inc.php b/lib/adodb/drivers/adodb-postgres7.inc.php index 59174f67884..b5e63acface 100644 --- a/lib/adodb/drivers/adodb-postgres7.inc.php +++ b/lib/adodb/drivers/adodb-postgres7.inc.php @@ -154,10 +154,7 @@ class ADODB_postgres7 extends ADODB_postgres64 { } } - /** - * @returns assoc array where keys are tables, and values are foreign keys - */ - function MetaForeignKeys($table, $owner=false, $upper=false) + public function metaForeignKeys($table, $owner = '', $upper = false, $associative = false) { # Regex isolates the 2 terms between parenthesis using subexpressions $regex = '^.*\((.*)\).*\((.*)\).*$'; @@ -193,50 +190,18 @@ class ADODB_postgres7 extends ADODB_postgres64 { $a = array(); while (!$rs->EOF) { + $lookup_table = $rs->fields('lookup_table'); + $fields = $rs->fields('dep_field') . '=' . $rs->fields('lookup_field'); 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')); + $lookup_table = strtoupper($lookup_table); + $fields = strtoupper($fields); } + $a[$lookup_table][] = str_replace('"','', $fields); + $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 - pg_trigger t,pg_class c,pg_proc p - WHERE - t.tgenabled AND - t.tgrelid = c.oid AND - t.tgfoid = p.oid AND - p.proname = \'RI_FKey_check_ins\' AND - c.relname = \''.strtolower($table).'\' - ORDER BY - t.tgrelid'; - - $rs = $this->Execute($sql); - - if (!$rs || $rs->EOF) return false; - - $arr = $rs->GetArray(); - $a = array(); - foreach($arr as $v) { - $data = explode(chr(0), $v['args']); - $size = count($data)-1; //-1 because the last node is empty - for($i = 4; $i < $size; $i++) { - if ($upper) - $a[strtoupper($data[2])][] = strtoupper($data[$i].'='.$data[++$i]); - else - $a[$data[2]][] = $data[$i].'='.$data[++$i]; - } - } - return $a; } function _query($sql,$inputarr=false) @@ -266,7 +231,7 @@ class ADODB_postgres7 extends ADODB_postgres64 { } // check if no data returned, then no need to create real recordset if ($rez && pg_num_fields($rez) <= 0) { - if (is_resource($this->_resultid) && get_resource_type($this->_resultid) === 'pgsql result') { + if ($this->_resultid !== false) { pg_free_result($this->_resultid); } $this->_resultid = $rez; @@ -334,10 +299,8 @@ class ADORecordSet_postgres7 extends ADORecordSet_postgres64{ if (!$this->EOF) { $this->_currentRow++; if ($this->_numOfRows < 0 || $this->_numOfRows > $this->_currentRow) { - $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); - - if (is_array($this->fields)) { - if ($this->fields && isset($this->_blobArr)) $this->_fixblobs(); + $this->_prepfields(); + if ($this->fields !== false) { return true; } } @@ -360,14 +323,13 @@ class ADORecordSet_assoc_postgres7 extends ADORecordSet_postgres64{ return false; } - $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); - - if ($this->fields) { - if (isset($this->_blobArr)) $this->_fixblobs(); + $this->_prepfields(); + if ($this->fields !== false) { $this->_updatefields(); + return true; } - return (is_array($this->fields)); + return false; } function MoveNext() @@ -375,19 +337,13 @@ class ADORecordSet_assoc_postgres7 extends ADORecordSet_postgres64{ if (!$this->EOF) { $this->_currentRow++; if ($this->_numOfRows < 0 || $this->_numOfRows > $this->_currentRow) { - $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); - - if (is_array($this->fields)) { - if ($this->fields) { - if (isset($this->_blobArr)) $this->_fixblobs(); - - $this->_updatefields(); - } + $this->_prepfields(); + if ($this->fields !== false) { + $this->_updatefields(); return true; } } - $this->fields = false; $this->EOF = true; } diff --git a/lib/adodb/drivers/adodb-sqlite3.inc.php b/lib/adodb/drivers/adodb-sqlite3.inc.php index 548727d8bce..318171a8c5e 100644 --- a/lib/adodb/drivers/adodb-sqlite3.inc.php +++ b/lib/adodb/drivers/adodb-sqlite3.inc.php @@ -24,6 +24,9 @@ // security - hide paths if (!defined('ADODB_DIR')) die(); +/** + * Class ADODB_sqlite3 + */ class ADODB_sqlite3 extends ADOConnection { var $databaseType = "sqlite3"; var $dataProvider = "sqlite"; @@ -34,10 +37,13 @@ class ADODB_sqlite3 extends ADOConnection { var $hasInsertID = true; /// supports autoincrement ID? var $hasAffectedRows = true; /// supports affected rows for update/delete? var $metaTablesSQL = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"; - var $sysDate = "adodb_date('Y-m-d')"; - var $sysTimeStamp = "adodb_date('Y-m-d H:i:s')"; + var $sysDate = "DATE('now','localtime')"; + var $sysTimeStamp = "DATETIME('now','localtime')"; var $fmtTimeStamp = "'Y-m-d H:i:s'"; + /** @var SQLite3 */ + var $_connectionID; + function ServerInfo() { $version = SQLite3::version(); @@ -51,7 +57,7 @@ class ADODB_sqlite3 extends ADOConnection { if ($this->transOff) { return true; } - $ret = $this->Execute("BEGIN TRANSACTION"); + $this->Execute("BEGIN TRANSACTION"); $this->transCnt += 1; return true; } @@ -95,6 +101,9 @@ class ADODB_sqlite3 extends ADOConnection { $t = strtoupper($t); + if (array_key_exists($t,$this->customActualTypes)) + return $this->customActualTypes[$t]; + /* * We are using the Sqlite affinity method here * @link https://www.sqlite.org/datatype3.html @@ -197,7 +206,7 @@ class ADODB_sqlite3 extends ADOConnection { return $arr; } - function metaForeignKeys( $table, $owner = FALSE, $upper = FALSE, $associative = FALSE ) + public function metaForeignKeys($table, $owner = '', $upper = false, $associative = false) { global $ADODB_FETCH_MODE; if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC @@ -214,7 +223,7 @@ class ADODB_sqlite3 extends ADOConnection { ) WHERE type != 'meta' AND sql NOTNULL - AND LOWER(name) ='" . strtolower($table) . "'"; + AND LOWER(name) ='" . strtolower($table) . "'"; $tableSql = $this->getOne($sql); @@ -304,8 +313,7 @@ class ADODB_sqlite3 extends ADOConnection { $this->_connectionID->createFunction('adodb_date2', 'adodb_date2', 2); } - - // returns true or false + /** @noinspection PhpUnusedParameterInspection */ function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) { if (empty($argHostname) && $argDatabasename) { @@ -317,7 +325,6 @@ class ADODB_sqlite3 extends ADOConnection { return true; } - // returns true or false function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) { // There's no permanent connect in SQLite3 @@ -394,7 +401,7 @@ class ADODB_sqlite3 extends ADOConnection { return false; } - function CreateSequence($seqname='adodbseq',$start=1) + function createSequence($seqname='adodbseq', $startID=1) { if (empty($this->_genSeqSQL)) { return false; @@ -403,8 +410,8 @@ class ADODB_sqlite3 extends ADOConnection { if (!$ok) { return false; } - $start -= 1; - return $this->Execute("insert into $seqname values($start)"); + $startID -= 1; + return $this->Execute("insert into $seqname values($startID)"); } var $_dropSeqSQL = 'drop table %s'; @@ -559,14 +566,13 @@ class ADODB_sqlite3 extends ADOConnection { * * This uses the more efficient strftime native function to process * - * @param str $fld The name of the field to process + * @param string $fld The name of the field to process * - * @return str The SQL Statement + * @return string The SQL Statement */ function month($fld) { - $x = "strftime('%m',$fld)"; - return $x; + return "strftime('%m',$fld)"; } /** @@ -574,13 +580,12 @@ class ADODB_sqlite3 extends ADOConnection { * * This uses the more efficient strftime native function to process * - * @param str $fld The name of the field to process + * @param string $fld The name of the field to process * - * @return str The SQL Statement + * @return string The SQL Statement */ function day($fld) { - $x = "strftime('%d',$fld)"; - return $x; + return "strftime('%d',$fld)"; } /** @@ -588,14 +593,116 @@ class ADODB_sqlite3 extends ADOConnection { * * This uses the more efficient strftime native function to process * - * @param str $fld The name of the field to process + * @param string $fld The name of the field to process * - * @return str The SQL Statement + * @return string The SQL Statement */ function year($fld) { - $x = "strftime('%Y',$fld)"; - return $x; + return "strftime('%Y',$fld)"; + } + + /** + * SQLite update for blob + * + * SQLite must be a fully prepared statement (all variables must be bound), + * so $where can either be an array (array params) or a string that we will + * do our best to unpack and turn into a prepared statement. + * + * @param string $table + * @param string $column + * @param string $val Blob value to set + * @param mixed $where An array of parameters (key => value pairs), + * or a string (where clause). + * @param string $blobtype ignored + * + * @return bool success + */ + function updateBlob($table, $column, $val, $where, $blobtype = 'BLOB') + { + if (is_array($where)) { + // We were passed a set of key=>value pairs + $params = $where; + } else { + // Given a where clause string, we have to disassemble the + // statements into keys and values + $params = array(); + $temp = preg_split('/(where|and)/i', $where); + $where = array_filter($temp); + + foreach ($where as $wValue) { + $wTemp = preg_split('/[= \']+/', $wValue); + $wTemp = array_filter($wTemp); + $wTemp = array_values($wTemp); + $params[$wTemp[0]] = $wTemp[1]; + } + } + + $paramWhere = array(); + foreach ($params as $bindKey => $bindValue) { + $paramWhere[] = $bindKey . '=?'; + } + + $sql = "UPDATE $table SET $column=? WHERE " + . implode(' AND ', $paramWhere); + + // Prepare the statement + $stmt = $this->_connectionID->prepare($sql); + + // Set the first bind value equal to value we want to update + if (!$stmt->bindValue(1, $val, SQLITE3_BLOB)) { + return false; + } + + // Build as many keys as available + $bindIndex = 2; + foreach ($params as $bindValue) { + if (is_integer($bindValue) || is_bool($bindValue) || is_float($bindValue)) { + $type = SQLITE3_NUM; + } elseif (is_object($bindValue)) { + // Assume a blob, this should never appear in + // the binding for a where statement anyway + $type = SQLITE3_BLOB; + } else { + $type = SQLITE3_TEXT; + } + + if (!$stmt->bindValue($bindIndex, $bindValue, $type)) { + return false; + } + + $bindIndex++; + } + + // Now execute the update. NB this is SQLite execute, not ADOdb + $ok = $stmt->execute(); + return is_object($ok); + } + + /** + * SQLite update for blob from a file + * + * @param string $table + * @param string $column + * @param string $path Filename containing blob data + * @param mixed $where {@see updateBlob()} + * @param string $blobtype ignored + * + * @return bool success + */ + function updateBlobFile($table, $column, $path, $where, $blobtype = 'BLOB') + { + if (!file_exists($path)) { + return false; + } + + // Read file information + $fileContents = file_get_contents($path); + if ($fileContents === false) + // Distinguish between an empty file and failure + return false; + + return $this->updateBlob($table, $column, $fileContents, $where, $blobtype); } } @@ -609,9 +716,12 @@ class ADORecordset_sqlite3 extends ADORecordSet { var $databaseType = "sqlite3"; var $bind = false; + /** @var SQLite3Result */ + var $_queryID; + + /** @noinspection PhpMissingParentConstructorInspection */ function __construct($queryID,$mode=false) { - if ($mode === false) { global $ADODB_FETCH_MODE; $mode = $ADODB_FETCH_MODE; diff --git a/lib/adodb/perf/perf-mysql.inc.php b/lib/adodb/perf/perf-mysql.inc.php index dae44229d4f..ed92c8051df 100644 --- a/lib/adodb/perf/perf-mysql.inc.php +++ b/lib/adodb/perf/perf-mysql.inc.php @@ -125,7 +125,7 @@ class perf_mysql extends adodb_perf{ /** * Returns a list of table statuses. - * + * * @param string $orderby Unused (compatibility with parent method) * @return string A formatted set of recordsets */ @@ -299,7 +299,7 @@ class perf_mysql extends adodb_perf{ var $optimizeTableHigh = 'OPTIMIZE TABLE %s'; /** - * @see adodb_perf#optimizeTable + * @see adodb_perf::optimizeTable() */ function optimizeTable( $table, $mode = ADODB_OPT_LOW) { diff --git a/lib/adodb/perf/perf-postgres.inc.php b/lib/adodb/perf/perf-postgres.inc.php index 315c17f5c28..16b767d206f 100644 --- a/lib/adodb/perf/perf-postgres.inc.php +++ b/lib/adodb/perf/perf-postgres.inc.php @@ -102,7 +102,7 @@ class perf_postgres extends adodb_perf{ var $optimizeTableHigh = 'VACUUM ANALYZE %s'; /** - * @see adodb_perf#optimizeTable + * @see adodb_perf::optimizeTable() */ function optimizeTable($table, $mode = ADODB_OPT_LOW) @@ -131,7 +131,7 @@ class perf_postgres extends adodb_perf{ if ($partial) { $sqlq = $this->conn->qstr($sql.'%'); - $arr = $this->conn->GetArray("select distinct distinct sql1 from adodb_logsql where sql1 like $sqlq"); + $arr = $this->conn->getArray("select distinct sql1 from adodb_logsql where sql1 like $sqlq"); if ($arr) { foreach($arr as $row) { $sql = reset($row); diff --git a/lib/adodb/perf/perf-sqlite3.inc.php b/lib/adodb/perf/perf-sqlite3.inc.php new file mode 100644 index 00000000000..19198d5b308 --- /dev/null +++ b/lib/adodb/perf/perf-sqlite3.inc.php @@ -0,0 +1,40 @@ +conn = $conn; + } + + function tables($orderby='1') + { + if (!$this->tablesSQL){ + return false; + } + + $rs = $this->conn->execute($this->tablesSQL); + if (!$rs) { + return false; + } + + $html = rs2html($rs, false, false, false, false); + return $html; + } +} diff --git a/lib/adodb/phpdoc.dist.xml b/lib/adodb/phpdoc.dist.xml new file mode 100644 index 00000000000..bbddd3db705 --- /dev/null +++ b/lib/adodb/phpdoc.dist.xml @@ -0,0 +1,24 @@ + + + + docs/api + docs/cache + + + + + + + scripts + tests + vendor/**/* + xsl + + + + diff --git a/lib/adodb/readme_moodle.txt b/lib/adodb/readme_moodle.txt index 7c64f5d3ce7..be981c8fbe1 100644 --- a/lib/adodb/readme_moodle.txt +++ b/lib/adodb/readme_moodle.txt @@ -1,4 +1,4 @@ -Description of ADOdb v5.21.4 library import into Moodle 4.0 and up +Description of ADOdb library import into Moodle Source: https://github.com/ADOdb/ADOdb @@ -16,13 +16,10 @@ Removed: * pear/ * replicate/ (if present) * scripts/ (if present) - * server.php + * server.php (if present) * session/ * tests/ (if present) Added: * index.html - prevent directory browsing on misconfigured servers * readme_moodle.txt - this file ;-) - -Our changes (to be checked on next update if they are already applied upstream): - * https://github.com/ADOdb/ADOdb/issues/791 diff --git a/lib/thirdpartylibs.xml b/lib/thirdpartylibs.xml index d76d534bcc0..db1a32b40c7 100644 --- a/lib/thirdpartylibs.xml +++ b/lib/thirdpartylibs.xml @@ -4,7 +4,7 @@ adodb AdoDB BSD/LGPL - 5.21.4 + 5.22.2 3-Clause/2.1+