diff options
44 files changed, 2613 insertions, 1372 deletions
diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..5dad2ea1 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ +# EditorConfig file for ADOdb +# https://editorconfig.org/ + +root = true + +# Default file settings +[*] +indent_style = tab +indent_size = 4 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true +charset = utf-8 + +# Python build scripts +[*.py] +indent_style = space + +# Markdown files +[*.md] +indent_style = space +trim_trailing_whitespace = false @@ -1,8 +1,9 @@ ADOdb Library for PHP ====================== -[](https://gitter.im/adodb/adodb?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[](https://sourceforge.net/projects/adodb/files/latest/download) +[](https://gitter.im/adodb/adodb?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[](https://sourceforge.net/projects/adodb/files/latest/download) +[](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/adodb-csvlib.inc.php b/adodb-csvlib.inc.php index c81254bf..6977c3dc 100644 --- a/adodb-csvlib.inc.php +++ b/adodb-csvlib.inc.php @@ -83,19 +83,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/adodb-datadict.inc.php b/adodb-datadict.inc.php index dbed95da..c73d5f2a 100644 --- a/adodb-datadict.inc.php +++ b/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; } @@ -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/adodb-memcache.lib.inc.php b/adodb-memcache.lib.inc.php index 7f110e74..16a0ba57 100644 --- a/adodb-memcache.lib.inc.php +++ b/adodb-memcache.lib.inc.php @@ -26,183 +26,416 @@ 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 +{ + /* + * 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; + /* + * populated with the proper library on connect + * and is used later when there are differences in specific calls + * between memcache and memcached + */ + private $memCacheLibrary = false; + + /* + * array of hosts + */ + private $hosts; + + /* + * Connection Port, uses default + */ + private $port = 11211; + + /* + * memcache compression with zlib + */ + private $compress = false; - //----------------------------- - // memcache specific variables + /* + * Array of options for memcached only + */ + private $options = false; + + /* + * Internal flag indicating successful connection + */ + private $isConnected = false; + + /* + * Handle for the Memcache library + */ + private $memcacheLibrary = false; + + /* + * New server feature controller lists available servers + */ + private $serverControllers = array(); + + /* + * New server feature controller uses granular + * server controller + */ + private $serverControllerTemplate = array( + 'host'=>'', + 'port'=>11211, + 'weight'=>0, + 'key'=>'' + ); - var $hosts; // array of hosts - var $port = 11211; - var $compress = false; // memcache compression with zlib - - var $_connected = false; - var $_memcache = false; + + /* + * An integer index into the libraries + */ + const MCLIB = 1; + const MCLIBD = 2; + + /* + * Xrefs the library flag to the actual class name + */ + private $libraries = array( + 1=>'Memcache', + 2=>'Memcached' + ); + + /* + * An indicator of which library we are using + */ + private $libraryFlag; + + /** + * constructor passes in a ADONewConnection Object + * + * @param $db ADONewConnection object + * + * @return obj + */ + public function __construct(&$db) + { + $this->hosts = $db->memCacheHost; + $this->port = $db->memCachePort; + $this->compress = $db->memCacheCompress; + $this->options = $db->memCacheOptions; + + } - function __construct(&$obj) + /** + * implement as 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::MCLIB; + else { - $this->hosts = $obj->memCacheHost; - $this->port = $obj->memCachePort; - $this->compress = $obj->memCacheCompress; + $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) + + $usedLibrary = $this->libraries[$this->libraryFlag]; + + $this->memCacheLibrary = new $usedLibrary; + if (!$this->memcacheLibrary) + { + $err = 'Memcache library failed to initialize'; + return false; + } + + /* + * Convert simple compression flag for memcached + */ + if ($this->libraryFlag == self::MCLIBD && $this->compress) + { + /* + * Value of Memcached::OPT_COMPRESSION = 2; + */ + $this->options[2] == 1; + } + /* + * Are there any options available for memcached + */ + if ($this->libraryFlag == self::MCLIBD && count($this->options) > 0) { - // 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!'; + $optionSuccess = $this->memCacheLibrary->setOptions($this->options); + if (!$optionSuccess) + { + $err = 'Invalid option parameters passed to Memecached'; return false; } - - 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; - } + } + + /* + * Have we passed a controller array + */ + if (!is_array($this->hosts)) + $this->hosts = array($this->hosts); + + if (array_values($this->hosts) == $this->hosts) + { + /* + * Old way, convert to controller + */ + foreach ($this->hosts as $ipAddress) + { + $connector = $this->serverControllerTemplate; + + $connector['host'] = $ipAddress; + $connector['port'] = $this->port; + + $this->serverControllers[] = $connector; } - if ($failcnt == sizeof($this->hosts)) { - $err = 'Can\'t connect to any memcache server'; - return false; + } + else + { + /* + * New way, must validate port, etc + */ + foreach ($this->hosts as $controller) + { + $connector = array_merge($this->serverControllerTemplate,$controller); + if ($this->libraryFlag == self::MCLIB) + { + /* + * Cannot use a key or weight in memcache, simply discard + */ + $connector['key'] = ''; + $connector['weight'] = 0; + + } + else + $connector['weight'] = $connector['weight'] ? (int)$connector['weight']:0; + + $this->serverControllers[] = $connector; } - $this->_connected = true; - $this->_memcache = $memcache; - return true; } - // returns true or false. true if successful save - function writecache($filename, $contents, $debug, $secs2cache) + /* + * Checks for existing connections ( but only for memcached ) + */ + if ($this->libraryFlag == self::MCLIBD) { - if (!$this->_connected) { - $err = ''; - if (!$this->connect($err) && $debug) ADOConnection::outp($err); + $existingServers = $memCache->getServerList(); + if (is_array($existingServers)) + { + /* + * Use the existing configuration + */ + $this->isConnected = true; + $this->memcacheLibrary = $memcache; + return true; } - if (!$this->_memcache) return false; - - $failed=false; - switch ($this->library) { - case 'Memcache': - if (!$this->_memcache->set($filename, $contents, $this->compress ? MEMCACHE_COMPRESSED : 0, $secs2cache)) { - $failed=true; - } - break; - case 'Memcached': - if (!$this->_memcache->set($filename, $contents, $secs2cache)) { - $failed=true; - } - break; + } + $failcnt = 0; + foreach($this->serverControllers as $controller) + { + switch($this->libraryFlag) + { + case self::MCLIB: + if (!@$this->memcacheLibrary->addServer($controller['host'],$controller['port'])) + $failcnt++; + break; default: - $failed=true; - break; - } - - if($failed) { - if ($debug) ADOConnection::outp(" Failed to save data at the memcache server!<br>\n"); - return false; + if (!@$this->memcacheLibrary->addServer($controller['host'],$controller['port'],$controller['weight'])) + $failcnt++; } - - return true; + } - - // returns a recordset - function readcache($filename, &$err, $secs2cache, $rsClass) + if ($failcnt == sizeof($this->serverControllers)) { - $false = false; - if (!$this->_connected) $this->connect($err); - if (!$this->_memcache) return $false; + $err = 'Can\'t connect to any memcache server'; + return false; - $rs = $this->_memcache->get($filename); - if (!$rs) { - $err = 'Item with such key doesn\'t exist on the memcache server.'; - return $false; - } + } + + /* + * A valid memcache connection is available + */ + $this->isConnected = true; + return true; + } - // 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 + /** + * 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; - $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; + $failed=false; + switch ($this->libraryFlag) + { + case self::MCLIB: + if (!$this->memcacheLibrary->set($filename, $contents, $this->compress ? MEMCACHE_COMPRESSED : 0, $secs2cache)) { + $failed=true; } - } - return $rs; + break; + case self::MCLIBD: + if (!$this->memcacheLibrary->set($filename, $contents, $secs2cache)) { + $failed=true; + } + break; + default: + $failed=true; + break; } - function flushall($debug=false) - { - if (!$this->_connected) { - $err = ''; - if (!$this->connect($err) && $debug) ADOConnection::outp($err); - } - if (!$this->_memcache) return false; + if($failed) { + if ($debug) ADOConnection::outp(" Failed to save data at the memcache server!<br>\n"); + return false; + } - $del = $this->_memcache->flush(); + return true; + } + + /** + * Reads a cached query to the server + * + * @param string $filename The MD5 of the query to read + * @param string $err The query results + * @param int $secs2cache + * @param obj $rsClass **UNUSED** + + * @return the record or false. + */ + public function readCache($filename, &$err, $secs2cache, $rsClass) + { + if (!$this->isConnected) $this->connect($err); + if (!$this->memcacheLibrary) + return false; - if ($debug) - if (!$del) ADOConnection::outp("flushall: failed!<br>\n"); - else ADOConnection::outp("flushall: succeeded!<br>\n"); + $rs = $this->memcacheLibrary->get($filename); + if (!$rs) + { + $err = 'Item with such key doesn\'t exist on the memcache server.'; + return false; + } - return $del; + // 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 - function flushcache($filename, $debug=false) + $tdiff = intval($rs->timeCreated+$secs2cache - time()); + if ($tdiff <= 2) { - if (!$this->_connected) { - $err = ''; - if (!$this->connect($err) && $debug) ADOConnection::outp($err); + 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; } - if (!$this->_memcache) return false; + } + return $rs; + } - $del = $this->_memcache->delete($filename); + /** + * Flushes all of the stored memcache data + * + * @param bool $debug + * + * @return int 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; - if ($debug) - if (!$del) ADOConnection::outp("flushcache: $key entry doesn't exist on memcache server!<br>\n"); - else ADOConnection::outp("flushcache: $key entry flushed from memcache server!<br>\n"); + $del = $this->memcacheLibrary->flush(); - return $del; - } + if ($debug) + if (!$del) + ADOConnection::outp("flushall: failed!<br>\n"); + else + ADOConnection::outp("flushall: succeeded!<br>\n"); + + return $del; + } - // not used for memcache - function createdir($dir, $hash) + /** + * Flushes the contents of a specified query + * + * @param str $filname The MD5 of the query to flush + * @param bool $debug + * + * @return int The response from the memcache server + */ + public function flushCache($filename, $debug=false) + { + if (!$this->isConnected) { - return true; + $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: $key entry doesn't exist on memcache server!<br>\n"); + else ADOConnection::outp("flushcache: $key entry flushed from memcache server!<br>\n"); + + return $del; } + +} diff --git a/adodb-xmlschema.inc.php b/adodb-xmlschema.inc.php index 58e3affd..7c4901df 100644 --- a/adodb-xmlschema.inc.php +++ b/adodb-xmlschema.inc.php @@ -26,20 +26,6 @@ * @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 */ @@ -126,7 +112,7 @@ class dbObject { /** * NOP */ - function __construct( &$parent, $attributes = NULL ) { + function __construct( $parent, $attributes = NULL ) { $this->parent = $parent; } @@ -255,7 +241,7 @@ class dbTable extends dbObject { * @param string $prefix DB Object prefix * @param array $attributes Array of table attributes. */ - function __construct( &$parent, $attributes = NULL ) { + function __construct( $parent, $attributes = NULL ) { $this->parent = $parent; $this->name = $this->prefix($attributes['NAME']); } @@ -651,7 +637,7 @@ class dbIndex extends dbObject { * * @internal */ - function __construct( &$parent, $attributes = NULL ) { + function __construct( $parent, $attributes = NULL ) { $this->parent = $parent; $this->name = $this->prefix ($attributes['NAME']); @@ -795,7 +781,7 @@ class dbData extends dbObject { * * @internal */ - function __construct( &$parent, $attributes = NULL ) { + function __construct( $parent, $attributes = NULL ) { $this->parent = $parent; } @@ -994,7 +980,7 @@ class dbQuerySet extends dbObject { * @param object $parent Parent object * @param array $attributes Attributes */ - function __construct( &$parent, $attributes = NULL ) { + function __construct( $parent, $attributes = NULL ) { $this->parent = $parent; // Overrides the manual prefix key @@ -1722,13 +1708,6 @@ 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. * @@ -1757,7 +1736,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 +1775,7 @@ class adoSchema { return FALSE; } - $schema = _file_get_contents( $schema ); + $schema = file_get_contents( $schema ); break; case 'string': default: @@ -1807,14 +1786,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); diff --git a/adodb-xmlschema03.inc.php b/adodb-xmlschema03.inc.php index de1ea26c..4ab6f89b 100644 --- a/adodb-xmlschema03.inc.php +++ b/adodb-xmlschema03.inc.php @@ -26,20 +26,6 @@ * @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 */ @@ -144,7 +130,7 @@ class dbObject { /** * NOP */ - function __construct( &$parent, $attributes = NULL ) { + function __construct( $parent, $attributes = NULL ) { $this->parent = $parent; } @@ -280,7 +266,7 @@ class dbTable extends dbObject { * @param string $prefix DB Object prefix * @param array $attributes Array of table attributes. */ - function __construct( &$parent, $attributes = NULL ) { + function __construct( $parent, $attributes = NULL ) { $this->parent = $parent; $this->name = $this->prefix($attributes['NAME']); } @@ -704,7 +690,7 @@ class dbIndex extends dbObject { * * @internal */ - function __construct( &$parent, $attributes = NULL ) { + function __construct( $parent, $attributes = NULL ) { $this->parent = $parent; $this->name = $this->prefix ($attributes['NAME']); @@ -849,7 +835,7 @@ class dbData extends dbObject { * * @internal */ - function __construct( &$parent, $attributes = NULL ) { + function __construct( $parent, $attributes = NULL ) { $this->parent = $parent; } @@ -1105,7 +1091,7 @@ class dbQuerySet extends dbObject { * @param object $parent Parent object * @param array $attributes Attributes */ - function __construct( &$parent, $attributes = NULL ) { + function __construct( $parent, $attributes = NULL ) { $this->parent = $parent; // Overrides the manual prefix key @@ -1893,14 +1879,6 @@ 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. * @@ -1929,7 +1907,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 +1946,7 @@ class adoSchema { return FALSE; } - $schema = _file_get_contents( $schema ); + $schema = file_get_contents( $schema ); break; case 'string': default: @@ -1979,14 +1957,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); diff --git a/adodb.inc.php b/adodb.inc.php index f15158f9..ea971b8a 100644 --- a/adodb.inc.php +++ b/adodb.inc.php @@ -198,7 +198,7 @@ if (!defined('_ADODB_LAYER')) { /** * ADODB version as a string. */ - $ADODB_vers = 'v5.21.2 2021-08-22'; + $ADODB_vers = 'v5.22.0-dev Unreleased'; /** * 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; @@ -440,11 +503,38 @@ 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 + ******************************************/ + /* + * Should we use memCache instead of caching in files + */ + public $memCache = false; + /* + * A string, array of hosts or array of memcache connection + * options (see adodb.org) + */ + public $memCacheHost; + + /* + * Default port, may be ignored if connection object array + * is set + */ + public $memCachePort = 11211; + + /* + * Use 'true' to store the item compressed + * uses zlib, Direct option for memcache, else + * For memcached, use the memcacheOptions feature + */ + public $memCacheCompress = false; + + /* + * If using mecached, an array of options + * @link https://www.php.net/manual/en/memcached.constants.php + */ + 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 +602,24 @@ 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 @@ -573,10 +681,53 @@ if (!defined('_ADODB_LAYER')) { } /** + * 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 +742,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 +887,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 +975,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; @@ -897,6 +1071,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 +1085,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 +1095,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; @@ -968,7 +1154,6 @@ if (!defined('_ADODB_LAYER')) { return $old; } - /** * PEAR DB Compat - do not use internally. * @@ -985,7 +1170,6 @@ if (!defined('_ADODB_LAYER')) { return $rs; } - /** * PEAR DB Compat - do not use internally */ @@ -1026,35 +1210,53 @@ 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; @@ -1936,16 +2138,6 @@ 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 @@ -2443,37 +2635,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; @@ -2642,7 +2844,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 */ @@ -2704,11 +2908,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; @@ -2716,9 +2923,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; @@ -3417,10 +3627,9 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 class ADOFetchObj { }; - //============================================================================================== - // CLASS ADORecordSet_empty - //============================================================================================== - + /** + * Class ADODB_Iterator_empty + */ class ADODB_Iterator_empty implements Iterator { private $rs; @@ -3543,10 +3752,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; @@ -3586,13 +3794,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 @@ -3613,8 +3822,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 */ @@ -3635,6 +3844,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() @@ -3644,7 +3857,7 @@ 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() * */ function __construct($queryID) { @@ -3671,7 +3884,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; @@ -3686,6 +3899,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. @@ -3824,24 +4047,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); } @@ -3862,11 +4089,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); } @@ -4079,8 +4306,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(); } @@ -4254,6 +4481,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. @@ -4327,8 +4562,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(); @@ -4364,15 +4600,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. @@ -4380,26 +4615,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 <pabloroca@mvps.org> + /** + * 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 <pabloroca@mvps.org> */ function PO_RecordCount($table="", $condition="") { @@ -4486,11 +4727,11 @@ 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 uppercase. * - * @param $isupper to set the object property names to uppercase + * @param bool $isUpper to set the object property names to uppercase * - * @return the object with the properties set to the fields of the current row + * @return ADOFetchObj The object with properties set to the fields of the current row */ - function FetchObject($isupper=true) { + function FetchObject($isUpper=true) { if (empty($this->_obj)) { $this->_obj = new ADOFetchObj(); $this->_names = array(); @@ -4499,12 +4740,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; @@ -4519,8 +4759,8 @@ 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 + * @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 */ @@ -4533,17 +4773,17 @@ 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 upper case field names. * - * @param $isupper to set the object property names to uppercase + * @param bool $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 + * @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) { + 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; @@ -4554,37 +4794,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', @@ -4691,8 +4923,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) { @@ -4708,7 +4938,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'; @@ -4717,8 +4947,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'; @@ -4769,8 +4999,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; } @@ -4779,6 +5011,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) { @@ -4787,6 +5021,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; @@ -4796,6 +5034,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) { @@ -4843,38 +5083,6 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 $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. diff --git a/datadict/datadict-access.inc.php b/datadict/datadict-access.inc.php index 7a7d4cbb..b3f9fad0 100644 --- a/datadict/datadict-access.inc.php +++ b/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/datadict/datadict-db2.inc.php b/datadict/datadict-db2.inc.php index c5dda09f..9ac106bb 100644 --- a/datadict/datadict-db2.inc.php +++ b/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/datadict/datadict-firebird.inc.php b/datadict/datadict-firebird.inc.php index 0020a0ae..79d0a8f5 100644 --- a/datadict/datadict-firebird.inc.php +++ b/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/datadict/datadict-generic.inc.php b/datadict/datadict-generic.inc.php index c9c8dee8..1a60dbc2 100644 --- a/datadict/datadict-generic.inc.php +++ b/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/datadict/datadict-ibase.inc.php b/datadict/datadict-ibase.inc.php index 5f58880f..4310ded4 100644 --- a/datadict/datadict-ibase.inc.php +++ b/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/datadict/datadict-informix.inc.php b/datadict/datadict-informix.inc.php index acb5ba74..9e151633 100644 --- a/datadict/datadict-informix.inc.php +++ b/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/datadict/datadict-mssql.inc.php b/datadict/datadict-mssql.inc.php index 1bcb27da..17df9e39 100644 --- a/datadict/datadict-mssql.inc.php +++ b/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/datadict/datadict-mssqlnative.inc.php b/datadict/datadict-mssqlnative.inc.php index b53dcd97..59228cbe 100644 --- a/datadict/datadict-mssqlnative.inc.php +++ b/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/datadict/datadict-mysql.inc.php b/datadict/datadict-mysql.inc.php index a1ee950a..9efbba1f 100644 --- a/datadict/datadict-mysql.inc.php +++ b/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/datadict/datadict-oci8.inc.php b/datadict/datadict-oci8.inc.php index 9a239095..6d2cd244 100644 --- a/datadict/datadict-oci8.inc.php +++ b/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/datadict/datadict-postgres.inc.php b/datadict/datadict-postgres.inc.php index 17627c44..219cc8a9 100644 --- a/datadict/datadict-postgres.inc.php +++ b/datadict/datadict-postgres.inc.php @@ -42,10 +42,17 @@ 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': @@ -99,8 +106,17 @@ class ADODB2_postgres extends ADODB_DataDict } } - 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; diff --git a/datadict/datadict-sapdb.inc.php b/datadict/datadict-sapdb.inc.php index 20c16aa6..c469800f 100644 --- a/datadict/datadict-sapdb.inc.php +++ b/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/datadict/datadict-sqlite.inc.php b/datadict/datadict-sqlite.inc.php index 942927f8..d565f887 100644 --- a/datadict/datadict-sqlite.inc.php +++ b/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/datadict/datadict-sybase.inc.php b/datadict/datadict-sybase.inc.php index e565f8e2..d6573dfa 100644 --- a/datadict/datadict-sybase.inc.php +++ b/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/docs/changelog.md b/docs/changelog.md index 9af8d376..2f5e9ee4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,39 @@ Older changelogs: ## [Unreleased] +### Added + +- Support for custom MetaTypes, e.g. JSON or GEOMETRY + [#602](https://github.com/ADOdb/ADOdb/issues/602) + [#626](https://github.com/ADOdb/ADOdb/issues/626) + [#649](https://github.com/ADOdb/ADOdb/issues/649) +- sqlite3 performance monitor stub + [#661](https://github.com/ADOdb/ADOdb/issues/661) +- Use of weighted server groups with Memcached + [#676](https://github.com/ADOdb/ADOdb/issues/676) + +### Changed + +- mysql: Support bound variable statements + [#655](https://github.com/ADOdb/ADOdb/issues/655) + +### Removed + +- Transpose() function and assorted sub-functions + [#586](https://github.com/ADOdb/ADOdb/issues/586) + +### Fixed + +- metaIndexes does not return primary key correctly + [#656](https://github.com/ADOdb/ADOdb/issues/656) +- mysql: Update socket and client flags for ssl + [#622](https://github.com/ADOdb/ADOdb/issues/622) +- pgsql: prevent AddColumnSQL() from updating existing values when default is changed + [#635](https://github.com/ADOdb/ADOdb/issues/635) + + +## [5.21.3] - Unreleased + ### Fixed - mysqli: force error reporting mode to OFF (PHP 8.1 compatibility) @@ -1129,6 +1162,7 @@ Released together with [v4.95](changelog_v4.x.md#495---17-may-2007) [Unreleased]: https://github.com/adodb/adodb/compare/v5.21.2...master +[5.21.3]: https://github.com/adodb/adodb/compare/v5.21.2...hotfix/5.21 [5.21.2]: https://github.com/adodb/adodb/compare/v5.21.1...v5.21.2 [5.21.1]: https://github.com/adodb/adodb/compare/v5.21.0...v5.21.1 [5.21.0]: https://github.com/adodb/adodb/compare/v5.21.0-rc.1...v5.21.0 diff --git a/drivers/adodb-ado.inc.php b/drivers/adodb-ado.inc.php index 67a032d7..fc000cec 100644 --- a/drivers/adodb-ado.inc.php +++ b/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/drivers/adodb-ado5.inc.php b/drivers/adodb-ado5.inc.php index f673d092..36f9c3b8 100644 --- a/drivers/adodb-ado5.inc.php +++ b/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/drivers/adodb-fbsql.inc.php b/drivers/adodb-fbsql.inc.php index 0fb895a1..a4255eb9 100644 --- a/drivers/adodb-fbsql.inc.php +++ b/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/drivers/adodb-firebird.inc.php b/drivers/adodb-firebird.inc.php index 2fafbe4f..e479077f 100644 --- a/drivers/adodb-firebird.inc.php +++ b/drivers/adodb-firebird.inc.php @@ -35,39 +35,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(''); } + /** + * 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) + { + $this->_transmode = $transaction_mode; + + if (empty($transaction_mode)) { + $this->_transmode = IBASE_WAIT | IBASE_REC_VERSION | IBASE_COMMITTED; + } - // returns true or false - function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$persist=false) + } + + /** + * 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; + 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 +149,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 +199,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 set to false to rollback transaction, true to commit + * + * @return true/false. + */ + public function commitTrans($ok=true) { if (!$ok) { return $this->RollbackTrans(); @@ -173,31 +273,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->_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 +301,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 +329,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 +351,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 +367,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(); - 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)); + if ($where) $where = ' where '.$where; + $rs = $this->execute("SELECT $col FROM $table $where FOR UPDATE WITH LOCK"); + return !empty($rs); } - function DropSequence($seqname = 'adodbseq') + /** + * 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) { - $seqname = strtoupper($seqname); - return $this->Execute("DROP GENERATOR $seqname"); + $sql = sprintf($this->_genSeqSQL,$seqname,$startID); + return $this->execute($sql); } - 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,36 +437,56 @@ 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? + /** + * Return the query id. + * + * @param string|array $sql + * @param array $iarr + * + * @return bool|object + */ function _query($sql,$iarr=false) { if ( !$this->isConnected() ) return false; @@ -353,7 +501,7 @@ class ADODB_firebird extends ADOConnection { $fn = 'fbird_execute'; $sql = $sql[1]; if (is_array($iarr)) { - if ( !isset($iarr[0]) ) + if ( !isset($iarr[0]) ) $iarr[0] = ''; // PHP5 compat hack $fnarr = array_merge( array($sql) , $iarr); $ret = call_user_func_array($fn,$fnarr); @@ -363,9 +511,9 @@ class ADODB_firebird extends ADOConnection { } } else { $fn = 'fbird_query'; - if (is_array($iarr)) + if (is_array($iarr)) { - if (sizeof($iarr) == 0) + if (sizeof($iarr) == 0) $iarr[0] = ''; // PHP5 compat hack $fnarr = array_merge( array($conn,$sql) , $iarr); $ret = call_user_func_array($fn,$fnarr); @@ -378,7 +526,7 @@ class ADODB_firebird extends ADOConnection { fbird_commit($this->_connectionID); } - $this->_handleerror(); + $this->_handleError(); return $ret; } @@ -398,122 +546,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) { + 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; - $fld->max_length = ($ftype == 7 ? 4 : 9); - } else { - $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; + } // switch + } else { 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'; + $fld->scale = $fscale; + $fld->max_length = ($ftype == 7 ? 4 : 9); } else { - $fld->type = 'date'; + $fld->type = ($ftype == 7 ? 'smallint' : 'integer'); } - 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 +680,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,40 +713,79 @@ 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 ) - { - $blobid = fbird_blob_open($this->_connectionID, $blob ); - $realblob = fbird_blob_get( $blobid,$this->maxblobsize); // 2nd param is max size of blob -- Kevin Boillet <kevinboillet@yahoo.fr> - while($string = fbird_blob_get($blobid, 8192)){ - $realblob .= $string; - } - fbird_blob_close( $blobid ); - - return( $realblob ); - } - function _BlobDecode( $blob ) + /** + * Auto function called on read of blob to decode + * + * @param string $blob Value to decode + * + * @return string Decoded blob + */ + public function _blobDecode( $blob ) { + $blob_data = fbird_blob_info($this->_connectionID, $blob ); $blobid = fbird_blob_open($this->_connectionID, $blob ); @@ -598,10 +803,24 @@ class ADODB_firebird extends ADOConnection { 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 +836,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') + /** + * 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); + $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; + // 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); - } + 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 ///////////////////////////////////////////////////////// + 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); + $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; } - function OldUpdateBlob($table,$column,$val,$where,$blobtype='BLOB') + /** + * 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) { - $blob_id = fbird_blob_create($this->_connectionID); - fbird_blob_add($blob_id, $val); - $blob_id_str = fbird_blob_close($blob_id); - return $this->Execute("UPDATE $table SET $column=(?) WHERE $where",array($blob_id_str)) != false; - } + if (!$col) + $col = 'CURRENT_TIMESTAMP'; - // 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) - { - if (!$col) $col = $this->sysDate; $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 +948,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 +994,89 @@ 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; + var $bind = false; + + /** + * @var ADOFieldObject[] Holds a cached version of the metadata + */ + private $fieldObjects = false; - function __construct($id,$mode=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; + global $ADODB_FETCH_MODE; - $this->fetchMode = ($mode === false) ? $ADODB_FETCH_MODE : $mode; - parent::__construct($id); + $this->fetchMode = ($mode === false) ? $ADODB_FETCH_MODE : $mode; + parent::__construct($id); } + /** - * 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) + * 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; + } + } + + $this->fieldObjectsRetrieved = true; + $this->fieldObjectsHaveBlob = false; + + $this->_numOfFields = fbird_num_fields($this->_queryID); + for ($fieldOffset = 0; $fieldOffset < $this->_numOfFields; $fieldOffset++) { + $fld = new ADOFieldObject; - $ibf = fbird_field_info($this->_queryID,$fieldOffset); + $ibf = fbird_field_info($this->_queryID, $fieldOffset); $name = empty($ibf['alias']) ? $ibf['name'] : $ibf['alias']; @@ -799,23 +1096,55 @@ 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[$fieldOffset] = $fld; + + $this->fieldObjectsIndex[$fld->name] = $fieldOffset; + + 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,9 +1152,30 @@ class ADORecordset_firebird extends ADORecordSet return false; } - function _fetch() + public function _fetch() { - $f = @fbird_fetch_row($this->_queryID); + $localNumeric = true; + 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); + } else { + if (ADODB_ASSOC_CASE == ADODB_ASSOC_CASE_UPPER) { + $f = array_change_key_case($f, CASE_UPPER); + } + } + + } + } else { + // Numeric result + $f = @fbird_fetch_row($this->_queryID); + } + if ($f === false) { $this->fields = false; return false; @@ -834,86 +1184,143 @@ class ADORecordset_firebird extends ADORecordSet // 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]); + /* + * Fix missing nulls, not sure why they would be missing + * + * + $nullTemplate = array_fill(0,$this->_numOfFields,null); + */ + /* + * Retrieved record is always numeric, use array_replace + * to inject missing keys + */ + //$f = array_replace($nullTemplate,$f); + + /* + * For optimal performance, only process if there + * is a possiblity of something to do + */ + if ($this->fieldObjectsHaveBlob || $rtrim) { + /* + * Create a closure for an efficient method of + * iterating over the elements + */ + $localFieldObjects = $this->fieldObjects; + $localFieldObjectIndex = $this->fieldObjectsIndex; + $localConnection = &$this->connection; + + $rowTransform = function ($value, $key) use ( + &$f, + $rtrim, + $localFieldObjects, + $localConnection, + $localNumeric, + $localFieldObjectIndex + ) { + if ($localNumeric) { + $localKey = $key; } else { - $f[$i] = null; + // Cross reference the associative key back to numeric + $localKey = $localFieldObjectIndex[strtolower($key)]; } - } 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); } - // 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()); + if (!$localNumeric && $this->fetchMode & ADODB_FETCH_NUM) { + // Creates a fetch both + $fNum = array_values($f); + $f = array_merge($f, $fNum); } + + $this->fields = $f; + 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; } - 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); + + if (array_key_exists($t, $this->connection->customActualTypes)) { + return $this->connection->customActualTypes[$t]; + } - case 'TIMESTAMP': - case 'DATE': return 'D'; - case 'TIME': return 'T'; - //case 'T': return 'T'; + switch ($t) { + case 'CHAR': + return 'C'; - //case 'L': return 'L'; - case 'INT': - case 'SHORT': - case 'INTEGER': return 'I'; - default: return ADODB_DEFAULT_METATYPE; + 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/drivers/adodb-ibase.inc.php b/drivers/adodb-ibase.inc.php index 69de30dc..81ab29b5 100644 --- a/drivers/adodb-ibase.inc.php +++ b/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/drivers/adodb-mssqlnative.inc.php b/drivers/adodb-mssqlnative.inc.php index 36dc5433..6d6a0068 100644 --- a/drivers/adodb-mssqlnative.inc.php +++ b/drivers/adodb-mssqlnative.inc.php @@ -1008,6 +1008,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/drivers/adodb-mysql.inc.php b/drivers/adodb-mysql.inc.php index f07c081b..ed5570c9 100644 --- a/drivers/adodb-mysql.inc.php +++ b/drivers/adodb-mysql.inc.php @@ -869,9 +869,15 @@ class ADORecordSet_mysql 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; // mysql max_length is not accurate - switch (strtoupper($t)) { + + switch ($t) { case 'STRING': case 'CHAR': case 'VARCHAR': diff --git a/drivers/adodb-mysqli.inc.php b/drivers/adodb-mysqli.inc.php index 160f5fa3..4364c9e2 100644 --- a/drivers/adodb-mysqli.inc.php +++ b/drivers/adodb-mysqli.inc.php @@ -81,6 +81,8 @@ class ADODB_mysqli extends ADOConnection { */ 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,7 +125,7 @@ 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 @@ -202,6 +204,8 @@ 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; @@ -574,7 +578,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)) { @@ -856,7 +860,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); @@ -1052,6 +1056,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 +1085,73 @@ 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) { + return $this->_execute($sql,false); + } + + if (!is_array($inputarr)) { + $inputarr = array($inputarr); + } + + if (!is_array($sql)) { + $typeString = ''; + $typeArray = array(''); //placeholder for type list + + foreach ($inputarr as $v) { + $typeArray[] = $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 + $typeArray[0] = $typeString; + + $ret = $this->_execute($sql,$typeArray); + if (!$ret) { + return $ret; + } + } else { + $ret = $this->_execute($sql,$inputarr); + } + return $ret; + } + + /** + * Return the query id. + * + * @param string|array $sql + * @param array $inputarr + * + * @return bool|mysqli_result + */ function _query($sql, $inputarr) { global $ADODB_COUNTRECS; @@ -1121,6 +1186,91 @@ class ADODB_mysqli extends ADOConnection { $ret = mysqli_stmt_execute($stmt); return $ret; } + 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; + + /* + * 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 != count($inputarr) - 1) { + $this->outp_throw( + "Input array has " . count($inputarr) . + " params, does not match query: '" . htmlspecialchars($sql) . "'", + 'Execute' + ); + return false; + } + + /* + * Must pass references into call_user_func_array + */ + $paramsByReference = array(); + foreach($inputarr as $key => $value) { + $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; + + /* + * Is the statement a non-select + */ + if ($stmt->affected_rows > -1) + { + $this->statementAffectedRows = $stmt->affected_rows; + return true; + } + /* + * Turn the statement into a result set + */ + $result = $stmt->get_result(); + /* + * Return the object for the select + */ + return $result; + + } else { /* @@ -1524,6 +1674,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 +1695,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 +1703,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 +1790,8 @@ class ADORecordSet_mysqli extends ADORecordSet{ case 'DEC': case 'FIXED': default: + + //if (!is_numeric($t)) echo "<p>--- Error in type matching $t -----</p>"; return 'N'; } @@ -1661,8 +1822,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/drivers/adodb-oci8.inc.php b/drivers/adodb-oci8.inc.php index e541b6b6..4dc93b1c 100644 --- a/drivers/adodb-oci8.inc.php +++ b/drivers/adodb-oci8.inc.php @@ -1809,8 +1809,13 @@ class ADORecordset_oci8 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]; - switch (strtoupper($t)) { + switch ($t) { case 'VARCHAR': case 'VARCHAR2': case 'CHAR': diff --git a/drivers/adodb-pdo.inc.php b/drivers/adodb-pdo.inc.php index cf8e4e26..7b676d3b 100644 --- a/drivers/adodb-pdo.inc.php +++ b/drivers/adodb-pdo.inc.php @@ -238,6 +238,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) { @@ -565,6 +585,7 @@ class ADODB_pdo extends ADOConnection { $this->_driver->debug = $this->debug; } if ($inputarr) { + /* * inputarr must be numeric */ diff --git a/drivers/adodb-pdo_oci.inc.php b/drivers/adodb-pdo_oci.inc.php index 9a05ee6a..20b47def 100644 --- a/drivers/adodb-pdo_oci.inc.php +++ b/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/drivers/adodb-pdo_pgsql.inc.php b/drivers/adodb-pdo_pgsql.inc.php index 0212d4fd..6c06ac10 100644 --- a/drivers/adodb-pdo_pgsql.inc.php +++ b/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/drivers/adodb-pdo_sqlite.inc.php b/drivers/adodb-pdo_sqlite.inc.php index b62ca35e..dab1de99 100644 --- a/drivers/adodb-pdo_sqlite.inc.php +++ b/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/drivers/adodb-postgres64.inc.php b/drivers/adodb-postgres64.inc.php index 9128467b..851b8b0a 100644 --- a/drivers/adodb-postgres64.inc.php +++ b/drivers/adodb-postgres64.inc.php @@ -1069,7 +1069,14 @@ 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': diff --git a/drivers/adodb-sqlite3.inc.php b/drivers/adodb-sqlite3.inc.php index 548727d8..7f945620 100644 --- a/drivers/adodb-sqlite3.inc.php +++ b/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 @@ -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; @@ -697,4 +807,4 @@ class ADORecordset_sqlite3 extends ADORecordSet { { } -} +}
\ No newline at end of file diff --git a/perf/perf-postgres.inc.php b/perf/perf-postgres.inc.php index 315c17f5..5ca19449 100644 --- a/perf/perf-postgres.inc.php +++ b/perf/perf-postgres.inc.php @@ -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/perf/perf-sqlite3.inc.php b/perf/perf-sqlite3.inc.php new file mode 100644 index 00000000..19198d5b --- /dev/null +++ b/perf/perf-sqlite3.inc.php @@ -0,0 +1,40 @@ +<?php + +// security - hide paths +if (!defined('ADODB_DIR')) die(); + +class perf_sqlite3 extends adodb_perf{ + + var $tablesSQL = "SELECT * FROM sqlite_master WHERE type='table'"; + + var $createTableSQL = "CREATE TABLE adodb_logsql ( + created datetime NOT NULL, + sql0 varchar(250) NOT NULL, + sql1 text NOT NULL, + params text NOT NULL, + tracer text NOT NULL, + timer decimal(16,6) NOT NULL + )"; + + var $settings = array(); + + function __construct(&$conn) + { + $this->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/server.php b/server.php deleted file mode 100644 index 76e757fd..00000000 --- a/server.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php -/** - * ADOdb Proxy Server. - * - * @deprecated 5.21.0 - * - * Security warning - use with extreme caution ! - * Depending on how it is setup, this feature can potentially expose the - * database to attacks, particularly if used with a privileged user account. - * - * This file is part of ADOdb, a Database Abstraction Layer library for PHP. - * - * @package ADOdb - * @link https://adodb.org Project's web site and documentation - * @link https://github.com/ADOdb/ADOdb Source code and issue tracker - * - * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause - * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option, - * any later version. This means you can use it in proprietary products. - * See the LICENSE.md file distributed with this source code for details. - * @license BSD-3-Clause - * @license LGPL-2.1-or-later - * - * @copyright 2000-2013 John Lim - * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community - */ - -/* Documentation on usage is at https://adodb.org/dokuwiki/doku.php?id=v5:proxy:proxy_index - * - * Legal query string parameters: - * - * sql = holds sql string - * nrows = number of rows to return - * offset = skip offset rows of data - * fetch = $ADODB_FETCH_MODE - * - * example: - * - * http://localhost/php/server.php?sql=select+*+from+table&nrows=10&offset=2 - */ - - -/* - * Define the IP address you want to accept requests from - * as a security measure. If blank we accept anyone promisciously! - */ -$ACCEPTIP = '127.0.0.1'; - -/* - * Connection parameters - */ -$driver = 'mysqli'; -$host = 'localhost'; // DSN for odbc -$uid = 'root'; -$pwd = 'garbase-it-is'; -$database = 'test'; - -/*============================ DO NOT MODIFY BELOW HERE =================================*/ -// $sep must match csv2rs() in adodb.inc.php -$sep = ' :::: '; - -include('./adodb.inc.php'); -include_once(ADODB_DIR.'/adodb-csvlib.inc.php'); - -function err($s) -{ - die('**** '.$s.' '); -} - -///////////////////////////////////////// DEFINITIONS - - -$remote = $_SERVER["REMOTE_ADDR"]; - - -if (!empty($ACCEPTIP)) - if ($remote != '127.0.0.1' && $remote != $ACCEPTIP) - err("Unauthorised client: '$remote'"); - - -if (empty($_REQUEST['sql'])) err('No SQL'); - - -$conn = ADONewConnection($driver); - -if (!$conn->connect($host,$uid,$pwd,$database)) err($conn->errorNo(). $sep . $conn->errorMsg()); -$sql = $_REQUEST['sql']; - -if (isset($_REQUEST['fetch'])) - $ADODB_FETCH_MODE = $_REQUEST['fetch']; - -if (isset($_REQUEST['nrows'])) { - $nrows = $_REQUEST['nrows']; - $offset = isset($_REQUEST['offset']) ? $_REQUEST['offset'] : -1; - $rs = $conn->selectLimit($sql,$nrows,$offset); -} else - $rs = $conn->execute($sql); -if ($rs){ - //$rs->timeToLive = 1; - echo _rs2serialize($rs,$conn,$sql); - $rs->close(); -} else - err($conn->errorNo(). $sep .$conn->errorMsg()); diff --git a/session/session_schema2.xml b/session/session_schema2.xml index 89cb4f28..a8a1b229 100644 --- a/session/session_schema2.xml +++ b/session/session_schema2.xml @@ -1,37 +1,33 @@ <?xml version="1.0"?> <schema version="0.3"> <table name="sessions2"> - <descr>table for ADOdb session-management</descr> - + <descr>Table for ADOdb session management</descr> <field name="SESSKEY" type="C" size="64"> - <descr>session key</descr> + <descr>Session key to identify a user's browser session.</descr> <KEY/> <NOTNULL/> </field> - - - <field name="EXPIRY" type="T"> - <descr></descr> + <descr>Slightly redundant as it can be dynamically calculated by NOW() and MODIFIED field, + but it enables forcing a fixed timeout for specific sessions. + </descr> <NOTNULL/> </field> - - <field name="CREATED" type="T"> - <descr></descr> + <field name="CREATED" type="T"> + <descr>New session creation Timestamp.</descr> <NOTNULL/> </field> - - <field name="MODIFIED" type="T"> - <descr></descr> + <field name="MODIFIED" type="T"> + <descr>Timestamp which is usually updated when the user interacts with a site, to extend the expire time.</descr> <NOTNULL/> </field> - <field name="EXPIREREF" type="C" size="250"> - <descr></descr> + <descr>Usually a User Id or unique username of your application. + The name EXPIREREF is a bit weird, it may be better to call it USERREF? + </descr> </field> - <field name="SESSDATA" type="XL"> - <descr></descr> + <descr>PHP's serialized session data or encrypted serialized session data.</descr> <NOTNULL/> </field> </table> diff --git a/tests/client.php b/tests/client.php deleted file mode 100644 index 8452da40..00000000 --- a/tests/client.php +++ /dev/null @@ -1,208 +0,0 @@ -<html> -<body bgcolor=white> -<?php -/** - * ADOdb tests - Proxy client - * - * @link https://adodb.org/dokuwiki/doku.php?id=v5:proxy:proxy_index - * - * This file is part of ADOdb, a Database Abstraction Layer library for PHP. - * - * @package ADOdb - * @link https://adodb.org Project's web site and documentation - * @link https://github.com/ADOdb/ADOdb Source code and issue tracker - * - * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause - * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option, - * any later version. This means you can use it in proprietary products. - * See the LICENSE.md file distributed with this source code for details. - * @license BSD-3-Clause - * @license LGPL-2.1-or-later - * - * @copyright 2000-2013 John Lim - * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community - */ - - echo PHP_VERSION,'<br>'; - var_dump(parse_url('odbc_mssql://userserver/')); - die(); - -include('../adodb.inc.php'); -include('../tohtml.inc.php'); - - function send2server($url,$sql) - { - $url .= '?sql='.urlencode($sql); - print "<p>$url</p>"; - $rs = csv2rs($url,$err); - if ($err) print $err; - return $rs; - } - - function print_pre($s) - { - print "<pre>";print_r($s);print "</pre>"; - } - - -$serverURL = 'http://localhost/php/phplens/adodb/server.php'; -$testhttp = false; - -$sql1 = "insertz into products (productname) values ('testprod 1')"; -$sql2 = "insert into products (productname) values ('testprod 1')"; -$sql3 = "insert into products (productname) values ('testprod 2')"; -$sql4 = "delete from products where productid>80"; -$sql5 = 'select * from products'; - -if ($testhttp) { - print "<a href=#c>Client Driver Tests</a><p>"; - print "<h3>Test Error</h3>"; - $rs = send2server($serverURL,$sql1); - print_pre($rs); - print "<hr />"; - - print "<h3>Test Insert</h3>"; - - $rs = send2server($serverURL,$sql2); - print_pre($rs); - print "<hr />"; - - print "<h3>Test Insert2</h3>"; - - $rs = send2server($serverURL,$sql3); - print_pre($rs); - print "<hr />"; - - print "<h3>Test Delete</h3>"; - - $rs = send2server($serverURL,$sql4); - print_pre($rs); - print "<hr />"; - - - print "<h3>Test Select</h3>"; - $rs = send2server($serverURL,$sql5); - if ($rs) rs2html($rs); - - print "<hr />"; -} - - -print "<a name=c><h1>CLIENT Driver Tests</h1>"; -$conn = ADONewConnection('csv'); -$conn->Connect($serverURL); -$conn->debug = true; - -print "<h3>Bad SQL</h3>"; - -$rs = $conn->Execute($sql1); - -print "<h3>Insert SQL 1</h3>"; -$rs = $conn->Execute($sql2); - -print "<h3>Insert SQL 2</h3>"; -$rs = $conn->Execute($sql3); - -print "<h3>Select SQL</h3>"; -$rs = $conn->Execute($sql5); -if ($rs) rs2html($rs); - -print "<h3>Delete SQL</h3>"; -$rs = $conn->Execute($sql4); - -print "<h3>Select SQL</h3>"; -$rs = $conn->Execute($sql5); -if ($rs) rs2html($rs); - - -/* EXPECTED RESULTS FOR HTTP TEST: - -Test Insert -http://localhost/php/adodb/server.php?sql=insert+into+products+%28productname%29+values+%28%27testprod%27%29 - -adorecordset Object -( - [dataProvider] => native - [fields] => - [blobSize] => 64 - [canSeek] => - [EOF] => 1 - [emptyTimeStamp] => - [emptyDate] => - [debug] => - [timeToLive] => 0 - [bind] => - [_numOfRows] => -1 - [_numOfFields] => 0 - [_queryID] => 1 - [_currentRow] => -1 - [_closed] => - [_inited] => - [sql] => insert into products (productname) values ('testprod') - [affectedrows] => 1 - [insertid] => 81 -) - - --------------------------------------------------------------------------------- - -Test Insert2 -http://localhost/php/adodb/server.php?sql=insert+into+products+%28productname%29+values+%28%27testprod%27%29 - -adorecordset Object -( - [dataProvider] => native - [fields] => - [blobSize] => 64 - [canSeek] => - [EOF] => 1 - [emptyTimeStamp] => - [emptyDate] => - [debug] => - [timeToLive] => 0 - [bind] => - [_numOfRows] => -1 - [_numOfFields] => 0 - [_queryID] => 1 - [_currentRow] => -1 - [_closed] => - [_inited] => - [sql] => insert into products (productname) values ('testprod') - [affectedrows] => 1 - [insertid] => 82 -) - - --------------------------------------------------------------------------------- - -Test Delete -http://localhost/php/adodb/server.php?sql=delete+from+products+where+productid%3E80 - -adorecordset Object -( - [dataProvider] => native - [fields] => - [blobSize] => 64 - [canSeek] => - [EOF] => 1 - [emptyTimeStamp] => - [emptyDate] => - [debug] => - [timeToLive] => 0 - [bind] => - [_numOfRows] => -1 - [_numOfFields] => 0 - [_queryID] => 1 - [_currentRow] => -1 - [_closed] => - [_inited] => - [sql] => delete from products where productid>80 - [affectedrows] => 2 - [insertid] => 0 -) - -[more stuff deleted] - . - . - . -*/ diff --git a/tests/testdatabases.inc.php b/tests/testdatabases.inc.php index 8c6358a4..84f0a3ce 100644 --- a/tests/testdatabases.inc.php +++ b/tests/testdatabases.inc.php @@ -32,7 +32,6 @@ <input type=checkbox name="testmysqli" value=1 <?php echo !empty($testmysqli) ? 'checked' : '' ?>> <b>MySQLi</b> <br> <td><input type=checkbox name="testsqlite" value=1 <?php echo !empty($testsqlite) ? 'checked' : '' ?>> <b>SQLite</b><br> -<input type=checkbox name="testproxy" value=1 <?php echo !empty($testproxy) ? 'checked' : '' ?>> <b>MySQL Proxy</b><br> <input type=checkbox name="testoracle" value=1 <?php echo !empty($testoracle) ? 'checked' : '' ?>> <b>Oracle (oci8)</b> <br> <input type=checkbox name="testpostgres" value=1 <?php echo !empty($testpostgres) ? 'checked' : '' ?>> <b>PostgreSQL</b><br> <input type=checkbox name="testpostgres9" value=1 <?php echo !empty($testpostgres9) ? 'checked' : '' ?>> <b>PostgreSQL 9</b><br> @@ -325,18 +324,6 @@ if (!empty($testmysqlodbc)) { // MYSQL else print "ERROR: MySQL test requires a MySQL server on localhost, userid='admin', password='', database='test'".'<BR>'.$db->ErrorMsg(); } -if (!empty($testproxy)){ - $db = ADONewConnection('proxy'); - print "<h1>Connecting $db->databaseType...</h1>"; - $server = 'localhost'; - - if ($db->PConnect('http://localhost/php/phplens/adodb/server.php')) - testdb($db, - "create table ADOXYZ (id int, firstname char(24), lastname char(24), created date) type=innodb"); - else print "ERROR: MySQL test requires a MySQL server on localhost, userid='admin', password='', database='test'".'<BR>'.$db->ErrorMsg(); - -} - ADOLoadCode('oci805'); ADOLoadCode("oci8po"); |
