Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]

   1  <?php
   2  /*
   3  @version   v5.21.0  2021-02-27
   4  @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
   5  @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
   6    Released under both BSD license and Lesser GPL library license.
   7    Whenever there is any discrepancy between the two licenses,
   8    the BSD license will take precedence.
   9    Set tabs to 8.
  10  
  11    This is the preferred driver for MySQL connections, and supports both transactional
  12    and non-transactional table types. You can use this as a drop-in replacement for both
  13    the mysql and mysqlt drivers. As of ADOdb Version 5.20.0, all other native MySQL drivers
  14    are deprecated
  15  
  16    Requires mysql client. Works on Windows and Unix.
  17  
  18  21 October 2003: MySQLi extension implementation by Arjen de Rijke (a.de.rijke@xs4all.nl)
  19  Based on adodb 3.40
  20  */
  21  
  22  // security - hide paths
  23  if (!defined('ADODB_DIR')) {
  24  	 die();
  25  }
  26  
  27  if (!defined("_ADODB_MYSQLI_LAYER")) {
  28  	 define("_ADODB_MYSQLI_LAYER", 1);
  29  
  30  // PHP5 compat...
  31  if (! defined("MYSQLI_BINARY_FLAG"))  define("MYSQLI_BINARY_FLAG", 128);
  32  if (!defined('MYSQLI_READ_DEFAULT_GROUP')) define('MYSQLI_READ_DEFAULT_GROUP',1);
  33  
  34  /**
  35   * Class ADODB_mysqli
  36   */
  37  class ADODB_mysqli extends ADOConnection {
  38  	 var $databaseType = 'mysqli';
  39  	 var $dataProvider = 'mysql';
  40  	 var $hasInsertID = true;
  41  	 var $hasAffectedRows = true;
  42  	 var $metaTablesSQL = "SELECT
  43  	 	 	 TABLE_NAME,
  44  	 	 	 CASE WHEN TABLE_TYPE = 'VIEW' THEN 'V' ELSE 'T' END
  45  	 	 FROM INFORMATION_SCHEMA.TABLES
  46  	 	 WHERE TABLE_SCHEMA=";
  47  	 var $metaColumnsSQL = "SHOW COLUMNS FROM `%s`";
  48  	 var $fmtTimeStamp = "'Y-m-d H:i:s'";
  49  	 var $hasLimit = true;
  50  	 var $hasMoveFirst = true;
  51  	 var $hasGenID = true;
  52  	 var $isoDates = true; // accepts dates in ISO format
  53  	 var $sysDate = 'CURDATE()';
  54  	 var $sysTimeStamp = 'NOW()';
  55  	 var $hasTransactions = true;
  56  	 var $forceNewConnect = false;
  57  	 var $poorAffectedRows = true;
  58  	 var $clientFlags = 0;
  59  	 var $substr = "substring";
  60  	 var $port = 3306; //Default to 3306 to fix HHVM bug
  61  	 var $socket = ''; //Default to empty string to fix HHVM bug
  62  	 var $_bindInputArray = false;
  63  	 var $nameQuote = '`';	 	 /// string to use to quote identifiers and names
  64  	 var $optionFlags = array(array(MYSQLI_READ_DEFAULT_GROUP,0));
  65  	 var $arrayClass = 'ADORecordSet_array_mysqli';
  66  	 var $multiQuery = false;
  67  	 var $ssl_key = null;
  68  	 var $ssl_cert = null;
  69  	 var $ssl_ca = null;
  70  	 var $ssl_capath = null;
  71  	 var $ssl_cipher = null;
  72  
  73  	 /**
  74  	  * Tells the insert_id method how to obtain the last value, depending on whether
  75  	  * we are using a stored procedure or not
  76  	  */
  77  	 private $usePreparedStatement = false;
  78  	 private $useLastInsertStatement = false;
  79  
  80  	 /**
  81  	  * Sets the isolation level of a transaction.
  82  	  *
  83  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:settransactionmode
  84  	  *
  85  	  * @param string $transaction_mode The transaction mode to set.
  86  	  *
  87  	  * @return void
  88  	  */
  89  	function SetTransactionMode($transaction_mode)
  90  	 {
  91  	 	 $this->_transmode = $transaction_mode;
  92  	 	 if (empty($transaction_mode)) {
  93  	 	 	 $this->execute('SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ');
  94  	 	 	 return;
  95  	 	 }
  96  	 	 if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode;
  97  	 	 $this->execute("SET SESSION TRANSACTION ".$transaction_mode);
  98  	 }
  99  
 100  	 /**
 101  	  * Connect to a database.
 102  	  *
 103  	  * @todo add: parameter int $port, parameter string $socket
 104  	  *
 105  	  * @param string|null $argHostname (Optional) The host to connect to.
 106  	  * @param string|null $argUsername (Optional) The username to connect as.
 107  	  * @param string|null $argPassword (Optional) The password to connect with.
 108  	  * @param string|null $argDatabasename (Optional) The name of the database to start in when connected.
 109  	  * @param bool $persist (Optional) Whether or not to use a persistent connection.
 110  	  *
 111  	  * @return bool|null True if connected successfully, false if connection failed, or null if the mysqli extension
 112  	  * isn't currently loaded.
 113  	  */
 114  	function _connect($argHostname = null,
 115  	 	 	 	 	   $argUsername = null,
 116  	 	 	 	 	   $argPassword = null,
 117  	 	 	 	 	   $argDatabasename = null,
 118  	 	 	 	 	   $persist = false)
 119  	 {
 120  	 	 if(!extension_loaded("mysqli")) {
 121  	 	 	 return null;
 122  	 	 }
 123  	 	 $this->_connectionID = @mysqli_init();
 124  
 125  	 	 if (is_null($this->_connectionID)) {
 126  	 	 	 // mysqli_init only fails if insufficient memory
 127  	 	 	 if ($this->debug) {
 128  	 	 	 	 ADOConnection::outp("mysqli_init() failed : "  . $this->errorMsg());
 129  	 	 	 }
 130  	 	 	 return false;
 131  	 	 }
 132  	 	 /*
 133  	 	 I suggest a simple fix which would enable adodb and mysqli driver to
 134  	 	 read connection options from the standard mysql configuration file
 135  	 	 /etc/my.cnf - "Bastien Duclaux" <bduclaux#yahoo.com>
 136  	 	 */
 137  	 	 $this->optionFlags = array();
 138  	 	 foreach($this->optionFlags as $arr) {
 139  	 	 	 mysqli_options($this->_connectionID,$arr[0],$arr[1]);
 140  	 	 }
 141  
 142  	 	 /*
 143  	 	 * Now merge in the standard connection parameters setting
 144  	 	 */
 145  	 	 foreach ($this->connectionParameters as $options)
 146  	 	 {
 147  	 	 	 foreach($options as $k=>$v)
 148  	 	 	 	 $ok = mysqli_options($this->_connectionID,$k,$v);
 149  	 	 }
 150  
 151  	 	 //https://php.net/manual/en/mysqli.persistconns.php
 152  	 	 if ($persist && strncmp($argHostname,'p:',2) != 0) {
 153  	 	 	 $argHostname = 'p:' . $argHostname;
 154  	 	 }
 155  
 156  	 	 // SSL Connections for MySQLI
 157  	 	 if ($this->ssl_key || $this->ssl_cert || $this->ssl_ca || $this->ssl_capath || $this->ssl_cipher) {
 158  	 	 	 mysqli_ssl_set($this->_connectionID, $this->ssl_key, $this->ssl_cert, $this->ssl_ca, $this->ssl_capath, $this->ssl_cipher);
 159  	 	 }
 160  
 161  	 	 #if (!empty($this->port)) $argHostname .= ":".$this->port;
 162  	 	 $ok = @mysqli_real_connect($this->_connectionID,
 163  	 	 	 	 	 $argHostname,
 164  	 	 	 	 	 $argUsername,
 165  	 	 	 	 	 $argPassword,
 166  	 	 	 	 	 $argDatabasename,
 167  	 	 	 	 	 # PHP7 compat: port must be int. Use default port if cast yields zero
 168  	 	 	 	 	 (int)$this->port != 0 ? (int)$this->port : 3306,
 169  	 	 	 	 	 $this->socket,
 170  	 	 	 	 	 $this->clientFlags);
 171  
 172  	 	 if ($ok) {
 173  	 	 	 if ($argDatabasename)  return $this->selectDB($argDatabasename);
 174  	 	 	 return true;
 175  	 	 } else {
 176  	 	 	 if ($this->debug) {
 177  	 	 	 	 ADOConnection::outp("Could not connect : "  . $this->errorMsg());
 178  	 	 	 }
 179  	 	 	 $this->_connectionID = null;
 180  	 	 	 return false;
 181  	 	 }
 182  	 }
 183  
 184  	 /**
 185  	  * Connect to a database with a persistent connection.
 186  	  *
 187  	  * @param string|null $argHostname The host to connect to.
 188  	  * @param string|null $argUsername The username to connect as.
 189  	  * @param string|null $argPassword The password to connect with.
 190  	  * @param string|null $argDatabasename The name of the database to start in when connected.
 191  	  *
 192  	  * @return bool|null True if connected successfully, false if connection failed, or null if the mysqli extension
 193  	  * isn't currently loaded.
 194  	  */
 195  	function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
 196  	 {
 197  	 	 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename, true);
 198  	 }
 199  
 200  	 /**
 201  	  * Connect to a database, whilst setting $this->forceNewConnect to true.
 202  	  *
 203  	  * When is this used? Close old connection first?
 204  	  * In _connect(), check $this->forceNewConnect?
 205  	  *
 206  	  * @param string|null $argHostname The host to connect to.
 207  	  * @param string|null $argUsername The username to connect as.
 208  	  * @param string|null $argPassword The password to connect with.
 209  	  * @param string|null $argDatabasename The name of the database to start in when connected.
 210  	  *
 211  	  * @return bool|null True if connected successfully, false if connection failed, or null if the mysqli extension
 212  	  * isn't currently loaded.
 213  	  */
 214  	function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
 215  	 {
 216  	 	 $this->forceNewConnect = true;
 217  	 	 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename);
 218  	 }
 219  
 220  	 /**
 221  	  * Replaces a null value with a specified replacement.
 222  	  *
 223  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:ifnull
 224  	  *
 225  	  * @param mixed $field The field in the table to check.
 226  	  * @param mixed $ifNull The value to replace the null value with if it is found.
 227  	  *
 228  	  * @return string
 229  	  */
 230  	function IfNull($field, $ifNull)
 231  	 {
 232  	 	 return " IFNULL($field, $ifNull) ";
 233  	 }
 234  
 235  	 /**
 236  	  * Retrieves the first column of the first matching row of an executed SQL statement.
 237  	  *
 238  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:getone
 239  	  *
 240  	  * @param string $sql The SQL to execute.
 241  	  * @param bool|array $inputarr (Optional) An array containing any required SQL parameters, or false if none needed.
 242  	  *
 243  	  * @return bool|array|null
 244  	  */
 245  	function GetOne($sql, $inputarr = false)
 246  	 {
 247  	 	 global $ADODB_GETONE_EOF;
 248  
 249  	 	 $ret = false;
 250  	 	 $rs = $this->execute($sql,$inputarr);
 251  	 	 if ($rs) {
 252  	 	 	 if ($rs->EOF) $ret = $ADODB_GETONE_EOF;
 253  	 	 	 else $ret = reset($rs->fields);
 254  	 	 	 $rs->close();
 255  	 	 }
 256  	 	 return $ret;
 257  	 }
 258  
 259  	 /**
 260  	  * Get information about the current MySQL server.
 261  	  *
 262  	  * @return array
 263  	  */
 264  	function ServerInfo()
 265  	 {
 266  	 	 $arr['description'] = $this->getOne("select version()");
 267  	 	 $arr['version'] = ADOConnection::_findvers($arr['description']);
 268  	 	 return $arr;
 269  	 }
 270  
 271  	 /**
 272  	  * Begins a granular transaction.
 273  	  *
 274  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:begintrans
 275  	  *
 276  	  * @return bool Always returns true.
 277  	  */
 278  	function BeginTrans()
 279  	 {
 280  	 	 if ($this->transOff) return true;
 281  	 	 $this->transCnt += 1;
 282  
 283  	 	 //$this->execute('SET AUTOCOMMIT=0');
 284  	 	 mysqli_autocommit($this->_connectionID, false);
 285  	 	 $this->execute('BEGIN');
 286  	 	 return true;
 287  	 }
 288  
 289  	 /**
 290  	  * Commits a granular transaction.
 291  	  *
 292  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:committrans
 293  	  *
 294  	  * @param bool $ok (Optional) If false, will rollback the transaction instead.
 295  	  *
 296  	  * @return bool Always returns true.
 297  	  */
 298  	function CommitTrans($ok = true)
 299  	 {
 300  	 	 if ($this->transOff) return true;
 301  	 	 if (!$ok) return $this->rollbackTrans();
 302  
 303  	 	 if ($this->transCnt) $this->transCnt -= 1;
 304  	 	 $this->execute('COMMIT');
 305  
 306  	 	 //$this->execute('SET AUTOCOMMIT=1');
 307  	 	 mysqli_autocommit($this->_connectionID, true);
 308  	 	 return true;
 309  	 }
 310  
 311  	 /**
 312  	  * Rollback a smart transaction.
 313  	  *
 314  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:rollbacktrans
 315  	  *
 316  	  * @return bool Always returns true.
 317  	  */
 318  	function RollbackTrans()
 319  	 {
 320  	 	 if ($this->transOff) return true;
 321  	 	 if ($this->transCnt) $this->transCnt -= 1;
 322  	 	 $this->execute('ROLLBACK');
 323  	 	 //$this->execute('SET AUTOCOMMIT=1');
 324  	 	 mysqli_autocommit($this->_connectionID, true);
 325  	 	 return true;
 326  	 }
 327  
 328  	 /**
 329  	  * Lock a table row for a duration of a transaction.
 330  	  *
 331  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:rowlock
 332  	  *
 333  	  * @param string $tables The table(s) to lock rows for.
 334  	  * @param string $where (Optional) The WHERE clause to use to determine which rows to lock.
 335  	  * @param string $col (Optional) The columns to select.
 336  	  *
 337  	  * @return bool True if the locking SQL statement executed successfully, otherwise false.
 338  	  */
 339  	function RowLock($tables, $where = '', $col = '1 as adodbignore')
 340  	 {
 341  	 	 if ($this->transCnt==0) $this->beginTrans();
 342  	 	 if ($where) $where = ' where '.$where;
 343  	 	 $rs = $this->execute("select $col from $tables $where for update");
 344  	 	 return !empty($rs);
 345  	 }
 346  
 347  	 /**
 348  	  * Appropriately quotes strings with ' characters for insertion into the database.
 349  	  *
 350  	  * Relies on mysqli_real_escape_string()
 351  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:qstr
 352  	  *
 353  	  * @param string $s            The string to quote
 354  	  * @param bool   $magic_quotes This param is not used since 5.21.0.
 355  	  *                             It remains for backwards compatibility.
 356  	  *
 357  	  * @return string Quoted string
 358  	  */
 359  	function qStr($s, $magic_quotes=false)
 360  	 {
 361  	 	 if (is_null($s)) {
 362  	 	 	 return 'NULL';
 363  	 	 }
 364  
 365  	 	 // mysqli_real_escape_string() throws a warning when the given
 366  	 	 // connection is invalid
 367  	 	 if ($this->_connectionID) {
 368  	 	 	 return "'" . mysqli_real_escape_string($this->_connectionID, $s) . "'";
 369  	 	 }
 370  
 371  	 	 if ($this->replaceQuote[0] == '\\') {
 372  	 	 	 $s = str_replace(array('\\', "\0"), array('\\\\', "\\\0") ,$s);
 373  	 	 }
 374  	 	 return "'" . str_replace("'", $this->replaceQuote, $s) . "'";
 375  	 }
 376  
 377  	 /**
 378  	  * Return the AUTO_INCREMENT id of the last row that has been inserted or updated in a table.
 379  	  *
 380  	  * @return int|string
 381  	  */
 382  	function _insertid()
 383  	 {
 384  	 	 // mysqli_insert_id does not return the last_insert_id if called after
 385  	 	 // execution of a stored procedure so we execute this instead.
 386  	 	 if ($this->useLastInsertStatement)
 387  	 	 	 $result = ADOConnection::getOne('SELECT LAST_INSERT_ID()');
 388  	 	 else
 389  	 	 	 $result = @mysqli_insert_id($this->_connectionID);
 390  
 391  	 	 if ($result == -1) {
 392  	 	 	 if ($this->debug)
 393  	 	 	 	 ADOConnection::outp("mysqli_insert_id() failed : "  . $this->errorMsg());
 394  	 	 }
 395  	 	 // reset prepared statement flags
 396  	 	 $this->usePreparedStatement   = false;
 397  	 	 $this->useLastInsertStatement = false;
 398  	 	 return $result;
 399  	 }
 400  
 401  	 /**
 402  	  * Returns how many rows were effected by the most recently executed SQL statement.
 403  	  * Only works for INSERT, UPDATE and DELETE queries.
 404  	  *
 405  	  * @return int The number of rows affected.
 406  	  */
 407  	function _affectedrows()
 408  	 {
 409  	 	 $result =  @mysqli_affected_rows($this->_connectionID);
 410  	 	 if ($result == -1) {
 411  	 	 	 if ($this->debug) ADOConnection::outp("mysqli_affected_rows() failed : "  . $this->errorMsg());
 412  	 	 }
 413  	 	 return $result;
 414  	 }
 415  
 416  	 // Reference on Last_Insert_ID on the recommended way to simulate sequences
 417  	 var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);";
 418  	 var $_genSeqSQL = "create table if not exists %s (id int not null)";
 419  	 var $_genSeqCountSQL = "select count(*) from %s";
 420  	 var $_genSeq2SQL = "insert into %s values (%s)";
 421  	 var $_dropSeqSQL = "drop table if exists %s";
 422  
 423  	 /**
 424  	  * Creates a sequence in the database.
 425  	  *
 426  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:createsequence
 427  	  *
 428  	  * @param string $seqname The sequence name.
 429  	  * @param int $startID The start id.
 430  	  *
 431  	  * @return ADORecordSet|bool A record set if executed successfully, otherwise false.
 432  	  */
 433  	function CreateSequence($seqname = 'adodbseq', $startID = 1)
 434  	 {
 435  	 	 if (empty($this->_genSeqSQL)) return false;
 436  
 437  	 	 $ok = $this->execute(sprintf($this->_genSeqSQL,$seqname));
 438  	 	 if (!$ok) return false;
 439  	 	 return $this->execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1));
 440  	 }
 441  
 442  	 /**
 443  	  * A portable method of creating sequence numbers.
 444  	  *
 445  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:genid
 446  	  *
 447  	  * @param string $seqname (Optional) The name of the sequence to use.
 448  	  * @param int $startID (Optional) The point to start at in the sequence.
 449  	  *
 450  	  * @return bool|int|string
 451  	  */
 452  	function GenID($seqname = 'adodbseq', $startID = 1)
 453  	 {
 454  	 	 // post-nuke sets hasGenID to false
 455  	 	 if (!$this->hasGenID) return false;
 456  
 457  	 	 $getnext = sprintf($this->_genIDSQL,$seqname);
 458  	 	 $holdtransOK = $this->_transOK; // save the current status
 459  	 	 $rs = @$this->execute($getnext);
 460  	 	 if (!$rs) {
 461  	 	 	 if ($holdtransOK) $this->_transOK = true; //if the status was ok before reset
 462  	 	 	 $this->execute(sprintf($this->_genSeqSQL,$seqname));
 463  	 	 	 $cnt = $this->getOne(sprintf($this->_genSeqCountSQL,$seqname));
 464  	 	 	 if (!$cnt) $this->execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1));
 465  	 	 	 $rs = $this->execute($getnext);
 466  	 	 }
 467  
 468  	 	 if ($rs) {
 469  	 	 	 $this->genID = mysqli_insert_id($this->_connectionID);
 470  	 	 	 if ($this->genID == 0) {
 471  	 	 	 	 $getnext = "select LAST_INSERT_ID() from " . $seqname;
 472  	 	 	 	 $rs = $this->execute($getnext);
 473  	 	 	 	 $this->genID = (int)$rs->fields[0];
 474  	 	 	 }
 475  	 	 	 $rs->close();
 476  	 	 } else
 477  	 	 	 $this->genID = 0;
 478  
 479  	 	 return $this->genID;
 480  	 }
 481  
 482  	 /**
 483  	  * Return a list of all visible databases except the 'mysql' database.
 484  	  *
 485  	  * @return array|false An array of database names, or false if the query failed.
 486  	  */
 487  	function MetaDatabases()
 488  	 {
 489  	 	 $query = "SHOW DATABASES";
 490  	 	 $ret = $this->execute($query);
 491  	 	 if ($ret && is_object($ret)){
 492  	 	 	 $arr = array();
 493  	 	 	 while (!$ret->EOF){
 494  	 	 	 	 $db = $ret->fields('Database');
 495  	 	 	 	 if ($db != 'mysql') $arr[] = $db;
 496  	 	 	 	 $ret->moveNext();
 497  	 	 	 }
 498  	 	 	 return $arr;
 499  	 	 }
 500  	 	 return $ret;
 501  	 }
 502  
 503  	 /**
 504  	  * Get a list of indexes on the specified table.
 505  	  *
 506  	  * @param string $table The name of the table to get indexes for.
 507  	  * @param bool $primary (Optional) Whether or not to include the primary key.
 508  	  * @param bool $owner (Optional) Unused.
 509  	  *
 510  	  * @return array|bool An array of the indexes, or false if the query to get the indexes failed.
 511  	  */
 512  	function MetaIndexes($table, $primary = false, $owner = false)
 513  	 {
 514  	 	 // save old fetch mode
 515  	 	 global $ADODB_FETCH_MODE;
 516  
 517  	 	 $false = false;
 518  	 	 $save = $ADODB_FETCH_MODE;
 519  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 520  	 	 if ($this->fetchMode !== FALSE) {
 521  	 	 	 $savem = $this->setFetchMode(FALSE);
 522  	 	 }
 523  
 524  	 	 // get index details
 525  	 	 $rs = $this->execute(sprintf('SHOW INDEXES FROM %s',$table));
 526  
 527  	 	 // restore fetchmode
 528  	 	 if (isset($savem)) {
 529  	 	 	 $this->setFetchMode($savem);
 530  	 	 }
 531  	 	 $ADODB_FETCH_MODE = $save;
 532  
 533  	 	 if (!is_object($rs)) {
 534  	 	 	 return $false;
 535  	 	 }
 536  
 537  	 	 $indexes = array ();
 538  
 539  	 	 // parse index data into array
 540  	 	 while ($row = $rs->fetchRow()) {
 541  	 	 	 if ($primary == FALSE AND $row[2] == 'PRIMARY') {
 542  	 	 	 	 continue;
 543  	 	 	 }
 544  
 545  	 	 	 if (!isset($indexes[$row[2]])) {
 546  	 	 	 	 $indexes[$row[2]] = array(
 547  	 	 	 	 	 'unique' => ($row[1] == 0),
 548  	 	 	 	 	 'columns' => array()
 549  	 	 	 	 );
 550  	 	 	 }
 551  
 552  	 	 	 $indexes[$row[2]]['columns'][$row[3] - 1] = $row[4];
 553  	 	 }
 554  
 555  	 	 // sort columns by order in the index
 556  	 	 foreach ( array_keys ($indexes) as $index )
 557  	 	 {
 558  	 	 	 ksort ($indexes[$index]['columns']);
 559  	 	 }
 560  
 561  	 	 return $indexes;
 562  	 }
 563  
 564  	 /**
 565  	  * Returns a portably-formatted date string from a timestamp database column.
 566  	  *
 567  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:sqldate
 568  	  *
 569  	  * @param string $fmt The date format to use.
 570  	  * @param string|bool $col (Optional) The table column to date format, or if false, use NOW().
 571  	  *
 572  	  * @return bool|string The SQL DATE_FORMAT() string, or false if the provided date format was empty.
 573  	  */
 574  	function SQLDate($fmt, $col = false)
 575  	 {
 576  	 	 if (!$col) $col = $this->sysTimeStamp;
 577  	 	 $s = 'DATE_FORMAT('.$col.",'";
 578  	 	 $concat = false;
 579  	 	 $len = strlen($fmt);
 580  	 	 for ($i=0; $i < $len; $i++) {
 581  	 	 	 $ch = $fmt[$i];
 582  	 	 	 switch($ch) {
 583  	 	 	 case 'Y':
 584  	 	 	 case 'y':
 585  	 	 	 	 $s .= '%Y';
 586  	 	 	 	 break;
 587  	 	 	 case 'Q':
 588  	 	 	 case 'q':
 589  	 	 	 	 $s .= "'),Quarter($col)";
 590  
 591  	 	 	 	 if ($len > $i+1) $s .= ",DATE_FORMAT($col,'";
 592  	 	 	 	 else $s .= ",('";
 593  	 	 	 	 $concat = true;
 594  	 	 	 	 break;
 595  	 	 	 case 'M':
 596  	 	 	 	 $s .= '%b';
 597  	 	 	 	 break;
 598  
 599  	 	 	 case 'm':
 600  	 	 	 	 $s .= '%m';
 601  	 	 	 	 break;
 602  	 	 	 case 'D':
 603  	 	 	 case 'd':
 604  	 	 	 	 $s .= '%d';
 605  	 	 	 	 break;
 606  
 607  	 	 	 case 'H':
 608  	 	 	 	 $s .= '%H';
 609  	 	 	 	 break;
 610  
 611  	 	 	 case 'h':
 612  	 	 	 	 $s .= '%I';
 613  	 	 	 	 break;
 614  
 615  	 	 	 case 'i':
 616  	 	 	 	 $s .= '%i';
 617  	 	 	 	 break;
 618  
 619  	 	 	 case 's':
 620  	 	 	 	 $s .= '%s';
 621  	 	 	 	 break;
 622  
 623  	 	 	 case 'a':
 624  	 	 	 case 'A':
 625  	 	 	 	 $s .= '%p';
 626  	 	 	 	 break;
 627  
 628  	 	 	 case 'w':
 629  	 	 	 	 $s .= '%w';
 630  	 	 	 	 break;
 631  
 632  	 	 	 case 'l':
 633  	 	 	 	 $s .= '%W';
 634  	 	 	 	 break;
 635  
 636  	 	 	 default:
 637  
 638  	 	 	 	 if ($ch == '\\') {
 639  	 	 	 	 	 $i++;
 640  	 	 	 	 	 $ch = substr($fmt,$i,1);
 641  	 	 	 	 }
 642  	 	 	 	 $s .= $ch;
 643  	 	 	 	 break;
 644  	 	 	 }
 645  	 	 }
 646  	 	 $s.="')";
 647  	 	 if ($concat) $s = "CONCAT($s)";
 648  	 	 return $s;
 649  	 }
 650  
 651  	 /**
 652  	  * Returns a database-specific concatenation of strings.
 653  	  *
 654  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:concat
 655  	  *
 656  	  * @return string
 657  	  */
 658  	function Concat()
 659  	 {
 660  	 	 $arr = func_get_args();
 661  
 662  	 	 // suggestion by andrew005@mnogo.ru
 663  	 	 $s = implode(',',$arr);
 664  	 	 if (strlen($s) > 0) return "CONCAT($s)";
 665  	 	 else return '';
 666  	 }
 667  
 668  	 /**
 669  	  * Creates a portable date offset field, for use in SQL statements.
 670  	  *
 671  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:offsetdate
 672  	  *
 673  	  * @param float $dayFraction A day in floating point
 674  	  * @param string|bool $date (Optional) The date to offset. If false, uses CURDATE()
 675  	  *
 676  	  * @return string
 677  	  */
 678  	function OffsetDate($dayFraction, $date = false)
 679  	 {
 680  	 	 if (!$date) $date = $this->sysDate;
 681  
 682  	 	 $fraction = $dayFraction * 24 * 3600;
 683  	 	 return $date . ' + INTERVAL ' .	  $fraction.' SECOND';
 684  
 685  //	 	 return "from_unixtime(unix_timestamp($date)+$fraction)";
 686  	 }
 687  
 688  	 /**
 689  	  * Returns information about stored procedures and stored functions.
 690  	  *
 691  	  * @param string|bool $NamePattern (Optional) Only look for procedures/functions with a name matching this pattern.
 692  	  * @param null $catalog (Optional) Unused.
 693  	  * @param null $schemaPattern (Optional) Unused.
 694  	  *
 695  	  * @return array
 696  	  */
 697  	function MetaProcedures($NamePattern = false, $catalog  = null, $schemaPattern  = null)
 698  	 {
 699  	 	 // save old fetch mode
 700  	 	 global $ADODB_FETCH_MODE;
 701  
 702  	 	 $save = $ADODB_FETCH_MODE;
 703  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 704  
 705  	 	 if ($this->fetchMode !== FALSE) {
 706  	 	 	 $savem = $this->setFetchMode(FALSE);
 707  	 	 }
 708  
 709  	 	 $procedures = array ();
 710  
 711  	 	 // get index details
 712  
 713  	 	 $likepattern = '';
 714  	 	 if ($NamePattern) {
 715  	 	 	 $likepattern = " LIKE '".$NamePattern."'";
 716  	 	 }
 717  	 	 $rs = $this->execute('SHOW PROCEDURE STATUS'.$likepattern);
 718  	 	 if (is_object($rs)) {
 719  
 720  	 	 	 // parse index data into array
 721  	 	 	 while ($row = $rs->fetchRow()) {
 722  	 	 	 	 $procedures[$row[1]] = array(
 723  	 	 	 	 	 'type' => 'PROCEDURE',
 724  	 	 	 	 	 'catalog' => '',
 725  	 	 	 	 	 'schema' => '',
 726  	 	 	 	 	 'remarks' => $row[7],
 727  	 	 	 	 );
 728  	 	 	 }
 729  	 	 }
 730  
 731  	 	 $rs = $this->execute('SHOW FUNCTION STATUS'.$likepattern);
 732  	 	 if (is_object($rs)) {
 733  	 	 	 // parse index data into array
 734  	 	 	 while ($row = $rs->fetchRow()) {
 735  	 	 	 	 $procedures[$row[1]] = array(
 736  	 	 	 	 	 'type' => 'FUNCTION',
 737  	 	 	 	 	 'catalog' => '',
 738  	 	 	 	 	 'schema' => '',
 739  	 	 	 	 	 'remarks' => $row[7]
 740  	 	 	 	 );
 741  	 	 	 }
 742  	 	 }
 743  
 744  	 	 // restore fetchmode
 745  	 	 if (isset($savem)) {
 746  	 	 	 	 $this->setFetchMode($savem);
 747  	 	 }
 748  	 	 $ADODB_FETCH_MODE = $save;
 749  
 750  	 	 return $procedures;
 751  	 }
 752  
 753  	 /**
 754  	  * Retrieves a list of tables based on given criteria
 755  	  *
 756  	  * @param string|bool $ttype (Optional) Table type = 'TABLE', 'VIEW' or false=both (default)
 757  	  * @param string|bool $showSchema (Optional) schema name, false = current schema (default)
 758  	  * @param string|bool $mask (Optional) filters the table by name
 759  	  *
 760  	  * @return array list of tables
 761  	  */
 762  	function MetaTables($ttype = false, $showSchema = false, $mask = false)
 763  	 {
 764  	 	 $save = $this->metaTablesSQL;
 765  	 	 if ($showSchema && is_string($showSchema)) {
 766  	 	 	 $this->metaTablesSQL .= $this->qstr($showSchema);
 767  	 	 } else {
 768  	 	 	 $this->metaTablesSQL .= "schema()";
 769  	 	 }
 770  
 771  	 	 if ($mask) {
 772  	 	 	 $mask = $this->qstr($mask);
 773  	 	 	 $this->metaTablesSQL .= " AND table_name LIKE $mask";
 774  	 	 }
 775  	 	 $ret = ADOConnection::metaTables($ttype,$showSchema);
 776  
 777  	 	 $this->metaTablesSQL = $save;
 778  	 	 return $ret;
 779  	 }
 780  
 781  	 /**
 782  	  * Return information about a table's foreign keys.
 783  	  *
 784  	  * @param string $table The name of the table to get the foreign keys for.
 785  	  * @param string|bool $owner (Optional) The database the table belongs to, or false to assume the current db.
 786  	  * @param string|bool $upper (Optional) Force uppercase table name on returned array keys.
 787  	  * @param bool $associative (Optional) Whether to return an associate or numeric array.
 788  	  *
 789  	  * @return array|bool An array of foreign keys, or false no foreign keys could be found.
 790  	  */
 791  	function MetaForeignKeys($table, $owner = false, $upper = false, $associative = false)
 792  	 {
 793  
 794  	 	 global $ADODB_FETCH_MODE;
 795  
 796  	 	 if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC
 797  	 	 || $this->fetchMode == ADODB_FETCH_ASSOC)
 798  	 	 	 $associative = true;
 799  
 800  	 	 $savem = $ADODB_FETCH_MODE;
 801  	 	 $this->setFetchMode(ADODB_FETCH_ASSOC);
 802  
 803  	 	 if ( !empty($owner) ) {
 804  	 	 	 $table = "$owner.$table";
 805  	 	 }
 806  
 807  	 	 $a_create_table = $this->getRow(sprintf('SHOW CREATE TABLE %s', $table));
 808  
 809  	 	 $this->setFetchMode($savem);
 810  
 811  	 	 $create_sql = isset($a_create_table["Create Table"]) ? $a_create_table["Create Table"] : $a_create_table["Create View"];
 812  
 813  	 	 $matches = array();
 814  
 815  	 	 if (!preg_match_all("/FOREIGN KEY \(`(.*?)`\) REFERENCES `(.*?)` \(`(.*?)`\)/", $create_sql, $matches)) return false;
 816  	 	 $foreign_keys = array();
 817  	 	 $num_keys = count($matches[0]);
 818  	 	 for ( $i = 0; $i < $num_keys; $i ++ ) {
 819  	 	 	 $my_field  = explode('`, `', $matches[1][$i]);
 820  	 	 	 $ref_table = $matches[2][$i];
 821  	 	 	 $ref_field = explode('`, `', $matches[3][$i]);
 822  
 823  	 	 	 if ( $upper ) {
 824  	 	 	 	 $ref_table = strtoupper($ref_table);
 825  	 	 	 }
 826  
 827  	 	 	 // see https://sourceforge.net/p/adodb/bugs/100/
 828  	 	 	 if (!isset($foreign_keys[$ref_table])) {
 829  	 	 	 	 $foreign_keys[$ref_table] = array();
 830  	 	 	 }
 831  	 	 	 $num_fields = count($my_field);
 832  	 	 	 for ( $j = 0; $j < $num_fields; $j ++ ) {
 833  	 	 	 	 if ( $associative ) {
 834  	 	 	 	 	 $foreign_keys[$ref_table][$ref_field[$j]] = $my_field[$j];
 835  	 	 	 	 } else {
 836  	 	 	 	 	 $foreign_keys[$ref_table][] = "{$my_field[$j]}={$ref_field[$j]}";
 837  	 	 	 	 }
 838  	 	 	 }
 839  	 	 }
 840  
 841  	 	 return $foreign_keys;
 842  	 }
 843  
 844  	 /**
 845  	  * Return an array of information about a table's columns.
 846  	  *
 847  	  * @param string $table The name of the table to get the column info for.
 848  	  * @param bool $normalize (Optional) Unused.
 849  	  *
 850  	  * @return ADOFieldObject[]|bool An array of info for each column, or false if it could not determine the info.
 851  	  */
 852  	function MetaColumns($table, $normalize = true)
 853  	 {
 854  	 	 $false = false;
 855  	 	 if (!$this->metaColumnsSQL)
 856  	 	 	 return $false;
 857  
 858  	 	 global $ADODB_FETCH_MODE;
 859  	 	 $save = $ADODB_FETCH_MODE;
 860  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 861  	 	 if ($this->fetchMode !== false)
 862  	 	 	 $savem = $this->SetFetchMode(false);
 863  	 	 /*
 864  	 	 * Return assoc array where key is column name, value is column type
 865  	 	 *    [1] => int unsigned
 866  	 	 */
 867  	 	 
 868  	 	 $SQL = "SELECT column_name, column_type 
 869  	 	 	 	   FROM information_schema.columns 
 870  	 	 	 	  WHERE table_schema='{$this->databaseName}' 
 871  	 	 	 	    AND table_name='$table'";
 872  	 	 
 873  	 	 $schemaArray = $this->getAssoc($SQL);
 874  	 	 $schemaArray = array_change_key_case($schemaArray,CASE_LOWER);
 875  	 
 876  	 	 $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table));
 877  	 	 if (isset($savem)) $this->SetFetchMode($savem);
 878  	 	 $ADODB_FETCH_MODE = $save;
 879  	 	 if (!is_object($rs))
 880  	 	 	 return $false;
 881  
 882  	 	 $retarr = array();
 883  	 	 while (!$rs->EOF) {
 884  	 	 	 $fld = new ADOFieldObject();
 885  	 	 	 $fld->name = $rs->fields[0];
 886  	 	 	 $type = $rs->fields[1];
 887  	 	 	 
 888  	 	 	 /*
 889  	 	 	 * Type from information_schema returns
 890  	 	 	 * the same format in V8 mysql as V5
 891  	 	 	 */
 892  	 	 	 $type = $schemaArray[strtolower($fld->name)];
 893  
 894  	 	 	 // split type into type(length):
 895  	 	 	 $fld->scale = null;
 896  	 	 	 if (preg_match("/^(.+)\((\d+),(\d+)/", $type, $query_array)) {
 897  	 	 	 	 $fld->type = $query_array[1];
 898  	 	 	 	 $fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1;
 899  	 	 	 	 $fld->scale = is_numeric($query_array[3]) ? $query_array[3] : -1;
 900  	 	 	 } elseif (preg_match("/^(.+)\((\d+)/", $type, $query_array)) {
 901  	 	 	 	 $fld->type = $query_array[1];
 902  	 	 	 	 $fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1;
 903  	 	 	 } elseif (preg_match("/^(enum)\((.*)\)$/i", $type, $query_array)) {
 904  	 	 	 	 $fld->type = $query_array[1];
 905  	 	 	 	 $arr = explode(",",$query_array[2]);
 906  	 	 	 	 $fld->enums = $arr;
 907  	 	 	 	 $zlen = max(array_map("strlen",$arr)) - 2; // PHP >= 4.0.6
 908  	 	 	 	 $fld->max_length = ($zlen > 0) ? $zlen : 1;
 909  	 	 	 } else {
 910  	 	 	 	 $fld->type = $type;
 911  	 	 	 	 $fld->max_length = -1;
 912  	 	 	 }
 913  	 	 	 
 914  	 	 	 $fld->not_null = ($rs->fields[2] != 'YES');
 915  	 	 	 $fld->primary_key = ($rs->fields[3] == 'PRI');
 916  	 	 	 $fld->auto_increment = (strpos($rs->fields[5], 'auto_increment') !== false);
 917  	 	 	 $fld->binary = (strpos($type,'blob') !== false);
 918  	 	 	 $fld->unsigned = (strpos($type,'unsigned') !== false);
 919  	 	 	 $fld->zerofill = (strpos($type,'zerofill') !== false);
 920  
 921  	 	 	 if (!$fld->binary) {
 922  	 	 	 	 $d = $rs->fields[4];
 923  	 	 	 	 if ($d != '' && $d != 'NULL') {
 924  	 	 	 	 	 $fld->has_default = true;
 925  	 	 	 	 	 $fld->default_value = $d;
 926  	 	 	 	 } else {
 927  	 	 	 	 	 $fld->has_default = false;
 928  	 	 	 	 }
 929  	 	 	 }
 930  
 931  	 	 	 if ($save == ADODB_FETCH_NUM) {
 932  	 	 	 	 $retarr[] = $fld;
 933  	 	 	 } else {
 934  	 	 	 	 $retarr[strtoupper($fld->name)] = $fld;
 935  	 	 	 }
 936  	 	 	 $rs->moveNext();
 937  	 	 }
 938  
 939  	 	 $rs->close();
 940  	 	 return $retarr;
 941  	 }
 942  
 943  	 /**
 944  	  * Select which database to connect to.
 945  	  *
 946  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:selectdb
 947  	  *
 948  	  * @param string $dbName The name of the database to select.
 949  	  *
 950  	  * @return bool True if the database was selected successfully, otherwise false.
 951  	  */
 952  	function SelectDB($dbName)
 953  	 {
 954  //	 	 $this->_connectionID = $this->mysqli_resolve_link($this->_connectionID);
 955  	 	 $this->database = $dbName;
 956  	 	 $this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions
 957  
 958  	 	 if ($this->_connectionID) {
 959  	 	 	 $result = @mysqli_select_db($this->_connectionID, $dbName);
 960  	 	 	 if (!$result) {
 961  	 	 	 	 ADOConnection::outp("Select of database " . $dbName . " failed. " . $this->errorMsg());
 962  	 	 	 }
 963  	 	 	 return $result;
 964  	 	 }
 965  	 	 return false;
 966  	 }
 967  
 968  	 /**
 969  	  * Executes a provided SQL statement and returns a handle to the result, with the ability to supply a starting
 970  	  * offset and record count.
 971  	  *
 972  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:selectlimit
 973  	  *
 974  	  * @param string $sql The SQL to execute.
 975  	  * @param int $nrows (Optional) The limit for the number of records you want returned. By default, all results.
 976  	  * @param int $offset (Optional) The offset to use when selecting the results. By default, no offset.
 977  	  * @param array|bool $inputarr (Optional) Any parameter values required by the SQL statement, or false if none.
 978  	  * @param int $secs (Optional) If greater than 0, perform a cached execute. By default, normal execution.
 979  	  *
 980  	  * @return ADORecordSet|false The query results, or false if the query failed to execute.
 981  	  */
 982  	function SelectLimit($sql,
 983  	 	 	 	 	 	  $nrows = -1,
 984  	 	 	 	 	 	  $offset = -1,
 985  	 	 	 	 	 	  $inputarr = false,
 986  	 	 	 	 	 	  $secs = 0)
 987  	 {
 988  	 	 $nrows = (int) $nrows;
 989  	 	 $offset = (int) $offset;
 990  	 	 $offsetStr = ($offset >= 0) ? "$offset," : '';
 991  	 	 if ($nrows < 0) $nrows = '18446744073709551615';
 992  
 993  	 	 if ($secs)
 994  	 	 	 $rs = $this->cacheExecute($secs, $sql . " LIMIT $offsetStr$nrows" , $inputarr );
 995  	 	 else
 996  	 	 	 $rs = $this->execute($sql . " LIMIT $offsetStr$nrows" , $inputarr );
 997  
 998  	 	 return $rs;
 999  	 }
1000  
1001  	 /**
1002  	  * Prepares an SQL statement and returns a handle to use.
1003  	  *
1004  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:prepare
1005  	  * @todo update this function to handle prepared statements correctly
1006  	  *
1007  	  * @param string $sql The SQL to prepare.
1008  	  *
1009  	  * @return string The original SQL that was provided.
1010  	  */
1011  	function Prepare($sql)
1012  	 {
1013  	 	 /*
1014  	 	 * Flag the insert_id method to use the correct retrieval method
1015  	 	 */
1016  	 	 $this->usePreparedStatement = true;
1017  
1018  	 	 /*
1019  	 	 * Prepared statements are not yet handled correctly
1020  	 	 */
1021  	 	 return $sql;
1022  	 	 $stmt = $this->_connectionID->prepare($sql);
1023  	 	 if (!$stmt) {
1024  	 	 	 echo $this->errorMsg();
1025  	 	 	 return $sql;
1026  	 	 }
1027  	 	 return array($sql,$stmt);
1028  	 }
1029  
1030  	 /**
1031  	  * Return the query id.
1032  	  *
1033  	  * @param string|array $sql
1034  	  * @param array $inputarr
1035  	  *
1036  	  * @return bool|mysqli_result
1037  	  */
1038  	function _query($sql, $inputarr)
1039  	 {
1040  	 	 global $ADODB_COUNTRECS;
1041  	 	 // Move to the next recordset, or return false if there is none. In a stored proc
1042  	 	 // call, mysqli_next_result returns true for the last "recordset", but mysqli_store_result
1043  	 	 // returns false. I think this is because the last "recordset" is actually just the
1044  	 	 // return value of the stored proc (ie the number of rows affected).
1045  	 	 // Commented out for reasons of performance. You should retrieve every recordset yourself.
1046  	 	 //	 if (!mysqli_next_result($this->connection->_connectionID))	 return false;
1047  
1048  	 	 if (is_array($sql)) {
1049  
1050  	 	 	 // Prepare() not supported because mysqli_stmt_execute does not return a recordset, but
1051  	 	 	 // returns as bound variables.
1052  
1053  	 	 	 $stmt = $sql[1];
1054  	 	 	 $a = '';
1055  	 	 	 foreach($inputarr as $k => $v) {
1056  	 	 	 	 if (is_string($v)) $a .= 's';
1057  	 	 	 	 else if (is_integer($v)) $a .= 'i';
1058  	 	 	 	 else $a .= 'd';
1059  	 	 	 }
1060  
1061  	 	 	 /*
1062  	 	 	  * set prepared statement flags
1063  	 	 	  */
1064  	 	 	 if ($this->usePreparedStatement)
1065  	 	 	 	 $this->useLastInsertStatement = true;
1066  
1067  	 	 	 $fnarr = array_merge( array($stmt,$a) , $inputarr);
1068  	 	 	 call_user_func_array('mysqli_stmt_bind_param',$fnarr);
1069  	 	 	 $ret = mysqli_stmt_execute($stmt);
1070  	 	 	 return $ret;
1071  	 	 }
1072  	 	 else
1073  	 	 {
1074  	 	 	 /*
1075  	 	 	 * reset prepared statement flags, in case we set them
1076  	 	 	 * previously and didn't use them
1077  	 	 	 */
1078  	 	 	 $this->usePreparedStatement   = false;
1079  	 	 	 $this->useLastInsertStatement = false;
1080  	 	 }
1081  
1082  	 	 /*
1083  	 	 if (!$mysql_res =  mysqli_query($this->_connectionID, $sql, ($ADODB_COUNTRECS) ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT)) {
1084  	 	 	 if ($this->debug) ADOConnection::outp("Query: " . $sql . " failed. " . $this->errorMsg());
1085  	 	 	 return false;
1086  	 	 }
1087  
1088  	 	 return $mysql_res;
1089  	 	 */
1090  
1091  	 	 if ($this->multiQuery) {
1092  	 	 	 $rs = mysqli_multi_query($this->_connectionID, $sql.';');
1093  	 	 	 if ($rs) {
1094  	 	 	 	 $rs = ($ADODB_COUNTRECS) ? @mysqli_store_result( $this->_connectionID ) : @mysqli_use_result( $this->_connectionID );
1095  	 	 	 	 return $rs ? $rs : true; // mysqli_more_results( $this->_connectionID )
1096  	 	 	 }
1097  	 	 } else {
1098  	 	 	 $rs = mysqli_query($this->_connectionID, $sql, $ADODB_COUNTRECS ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT);
1099  
1100  	 	 	 if ($rs) return $rs;
1101  	 	 }
1102  
1103  	 	 if($this->debug)
1104  	 	 	 ADOConnection::outp("Query: " . $sql . " failed. " . $this->errorMsg());
1105  
1106  	 	 return false;
1107  
1108  	 }
1109  
1110  	 /**
1111  	  * Returns a database specific error message.
1112  	  *
1113  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:errormsg
1114  	  *
1115  	  * @return string The last error message.
1116  	  */
1117  	function ErrorMsg()
1118  	 {
1119  	 	 if (empty($this->_connectionID))
1120  	 	 	 $this->_errorMsg = @mysqli_connect_error();
1121  	 	 else
1122  	 	 	 $this->_errorMsg = @mysqli_error($this->_connectionID);
1123  	 	 return $this->_errorMsg;
1124  	 }
1125  
1126  	 /**
1127  	  * Returns the last error number from previous database operation.
1128  	  *
1129  	  * @return int The last error number.
1130  	  */
1131  	function ErrorNo()
1132  	 {
1133  	 	 if (empty($this->_connectionID))
1134  	 	 	 return @mysqli_connect_errno();
1135  	 	 else
1136  	 	 	 return @mysqli_errno($this->_connectionID);
1137  	 }
1138  
1139  	 /**
1140  	  * Close the database connection.
1141  	  *
1142  	  * @return void
1143  	  */
1144  	function _close()
1145  	 {
1146  	 	 if($this->_connectionID) {
1147  	 	 	 mysqli_close($this->_connectionID);
1148  	 	 }
1149  	 	 $this->_connectionID = false;
1150  	 }
1151  
1152  	 /**
1153  	  * Returns the largest length of data that can be inserted into a character field.
1154  	  *
1155  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:charmax
1156  	  *
1157  	  * @return int
1158  	  */
1159  	function CharMax()
1160  	 {
1161  	 	 return 255;
1162  	 }
1163  
1164  	 /**
1165  	  * Returns the largest length of data that can be inserted into a text field.
1166  	  *
1167  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:textmax
1168  	  *
1169  	  * @return int
1170  	  */
1171  	function TextMax()
1172  	 {
1173  	 	 return 4294967295;
1174  	 }
1175  
1176  	 /**
1177  	  * Get the name of the character set the client connection is using now.
1178  	  *
1179  	  * @return string|bool The name of the character set, or false if it can't be determined.
1180  	  */
1181  	function GetCharSet()
1182  	 {
1183  	 	 //we will use ADO's builtin property charSet
1184  	 	 if (!method_exists($this->_connectionID,'character_set_name'))
1185  	 	 	 return false;
1186  
1187  	 	 $this->charSet = @$this->_connectionID->character_set_name();
1188  	 	 if (!$this->charSet) {
1189  	 	 	 return false;
1190  	 	 } else {
1191  	 	 	 return $this->charSet;
1192  	 	 }
1193  	 }
1194  
1195  	 /**
1196  	  * Sets the character set for database connections (limited databases).
1197  	  *
1198  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:setcharset
1199  	  *
1200  	  * @param string $charset_name The character set to switch to.
1201  	  *
1202  	  * @return bool True if the character set was changed successfully, otherwise false.
1203  	  */
1204  	function SetCharSet($charset_name)
1205  	 {
1206  	 	 if (!method_exists($this->_connectionID,'set_charset')) {
1207  	 	 	 return false;
1208  	 	 }
1209  
1210  	 	 if ($this->charSet !== $charset_name) {
1211  	 	 	 $if = @$this->_connectionID->set_charset($charset_name);
1212  	 	 	 return ($if === true & $this->getCharSet() == $charset_name);
1213  	 	 } else {
1214  	 	 	 return true;
1215  	 	 }
1216  	 }
1217  
1218  }
1219  
1220  /**
1221   * Class ADORecordSet_mysqli
1222   */
1223  class ADORecordSet_mysqli extends ADORecordSet{
1224  
1225  	 var $databaseType = "mysqli";
1226  	 var $canSeek = true;
1227  
1228  	function __construct($queryID, $mode = false)
1229  	 {
1230  	 	 if ($mode === false) {
1231  	 	 	 global $ADODB_FETCH_MODE;
1232  	 	 	 $mode = $ADODB_FETCH_MODE;
1233  	 	 }
1234  
1235  	 	 switch ($mode) {
1236  	 	 	 case ADODB_FETCH_NUM:
1237  	 	 	 	 $this->fetchMode = MYSQLI_NUM;
1238  	 	 	 	 break;
1239  	 	 	 case ADODB_FETCH_ASSOC:
1240  	 	 	 	 $this->fetchMode = MYSQLI_ASSOC;
1241  	 	 	 	 break;
1242  	 	 	 case ADODB_FETCH_DEFAULT:
1243  	 	 	 case ADODB_FETCH_BOTH:
1244  	 	 	 default:
1245  	 	 	 	 $this->fetchMode = MYSQLI_BOTH;
1246  	 	 	 	 break;
1247  	 	 }
1248  	 	 $this->adodbFetchMode = $mode;
1249  	 	 parent::__construct($queryID);
1250  	 }
1251  
1252  	function _initrs()
1253  	 {
1254  	 global $ADODB_COUNTRECS;
1255  
1256  	 	 $this->_numOfRows = $ADODB_COUNTRECS ? @mysqli_num_rows($this->_queryID) : -1;
1257  	 	 $this->_numOfFields = @mysqli_num_fields($this->_queryID);
1258  	 }
1259  
1260  /*
1261  1      = MYSQLI_NOT_NULL_FLAG
1262  2      = MYSQLI_PRI_KEY_FLAG
1263  4      = MYSQLI_UNIQUE_KEY_FLAG
1264  8      = MYSQLI_MULTIPLE_KEY_FLAG
1265  16     = MYSQLI_BLOB_FLAG
1266  32     = MYSQLI_UNSIGNED_FLAG
1267  64     = MYSQLI_ZEROFILL_FLAG
1268  128    = MYSQLI_BINARY_FLAG
1269  256    = MYSQLI_ENUM_FLAG
1270  512    = MYSQLI_AUTO_INCREMENT_FLAG
1271  1024   = MYSQLI_TIMESTAMP_FLAG
1272  2048   = MYSQLI_SET_FLAG
1273  32768  = MYSQLI_NUM_FLAG
1274  16384  = MYSQLI_PART_KEY_FLAG
1275  32768  = MYSQLI_GROUP_FLAG
1276  65536  = MYSQLI_UNIQUE_FLAG
1277  131072 = MYSQLI_BINCMP_FLAG
1278  */
1279  
1280  	 /**
1281  	  * Returns raw, database specific information about a field.
1282  	  *
1283  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:recordset:fetchfield
1284  	  *
1285  	  * @param int $fieldOffset (Optional) The field number to get information for.
1286  	  *
1287  	  * @return ADOFieldObject|bool
1288  	  */
1289  	function FetchField($fieldOffset = -1)
1290  	 {
1291  	 	 $fieldnr = $fieldOffset;
1292  	 	 if ($fieldOffset != -1) {
1293  	 	 	 $fieldOffset = @mysqli_field_seek($this->_queryID, $fieldnr);
1294  	 	 }
1295  	 	 $o = @mysqli_fetch_field($this->_queryID);
1296  	 	 if (!$o) return false;
1297  
1298  	 	 //Fix for HHVM
1299  	 	 if ( !isset($o->flags) ) {
1300  	 	 	 $o->flags = 0;
1301  	 	 }
1302  	 	 /* Properties of an ADOFieldObject as set by MetaColumns */
1303  	 	 $o->primary_key = $o->flags & MYSQLI_PRI_KEY_FLAG;
1304  	 	 $o->not_null = $o->flags & MYSQLI_NOT_NULL_FLAG;
1305  	 	 $o->auto_increment = $o->flags & MYSQLI_AUTO_INCREMENT_FLAG;
1306  	 	 $o->binary = $o->flags & MYSQLI_BINARY_FLAG;
1307  	 	 // $o->blob = $o->flags & MYSQLI_BLOB_FLAG; /* not returned by MetaColumns */
1308  	 	 $o->unsigned = $o->flags & MYSQLI_UNSIGNED_FLAG;
1309  
1310  	 	 /*
1311  	 	 * Trivial method to cast class to ADOfieldObject
1312  	 	 */
1313  	 	 $a = new ADOFieldObject;
1314  	 	 foreach (get_object_vars($o) as $key => $name)
1315  	 	 	 $a->$key = $name;
1316  	 	 return $a;
1317  	 }
1318  
1319  	 /**
1320  	  * Reads a row in associative mode if the recordset fetch mode is numeric.
1321  	  * Using this function when the fetch mode is set to ADODB_FETCH_ASSOC may produce unpredictable results.
1322  	  *
1323  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:getrowassoc
1324  	  *
1325  	  * @param int $upper Indicates whether the keys of the recordset should be upper case or lower case.
1326  	  *
1327  	  * @return array|bool
1328  	  */
1329  	function GetRowAssoc($upper = ADODB_ASSOC_CASE)
1330  	 {
1331  	 	 if ($this->fetchMode == MYSQLI_ASSOC && $upper == ADODB_ASSOC_CASE_LOWER) {
1332  	 	 	 return $this->fields;
1333  	 	 }
1334  	 	 $row = ADORecordSet::getRowAssoc($upper);
1335  	 	 return $row;
1336  	 }
1337  
1338  	 /**
1339  	  * Returns a single field in a single row of the current recordset.
1340  	  *
1341  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:recordset:fields
1342  	  *
1343  	  * @param string $colname The name of the field to retrieve.
1344  	  *
1345  	  * @return mixed
1346  	  */
1347  	function Fields($colname)
1348  	 {
1349  	 	 if ($this->fetchMode != MYSQLI_NUM) {
1350  	 	 	 return @$this->fields[$colname];
1351  	 	 }
1352  
1353  	 	 if (!$this->bind) {
1354  	 	 	 $this->bind = array();
1355  	 	 	 for ($i = 0; $i < $this->_numOfFields; $i++) {
1356  	 	 	 	 $o = $this->fetchField($i);
1357  	 	 	 	 $this->bind[strtoupper($o->name)] = $i;
1358  	 	 	 }
1359  	 	 }
1360  	 	 return $this->fields[$this->bind[strtoupper($colname)]];
1361  	 }
1362  
1363  	 /**
1364  	  * Adjusts the result pointer to an arbitrary row in the result.
1365  	  *
1366  	  * @param int $row The row to seek to.
1367  	  *
1368  	  * @return bool False if the recordset contains no rows, otherwise true.
1369  	  */
1370  	function _seek($row)
1371  	 {
1372  	 	 if ($this->_numOfRows == 0 || $row < 0) {
1373  	 	 	 return false;
1374  	 	 }
1375  
1376  	 	 mysqli_data_seek($this->_queryID, $row);
1377  	 	 $this->EOF = false;
1378  	 	 return true;
1379  	 }
1380  
1381  	 /**
1382  	  * In databases that allow accessing of recordsets, retrieves the next set.
1383  	  *
1384  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:recordset:nextrecordset
1385  	  *
1386  	  * @return bool
1387  	  */
1388  	function NextRecordSet()
1389  	 {
1390  	 	 global $ADODB_COUNTRECS;
1391  
1392  	 	 mysqli_free_result($this->_queryID);
1393  	 	 $this->_queryID = -1;
1394  	 	 // Move to the next recordset, or return false if there is none. In a stored proc
1395  	 	 // call, mysqli_next_result returns true for the last "recordset", but mysqli_store_result
1396  	 	 // returns false. I think this is because the last "recordset" is actually just the
1397  	 	 // return value of the stored proc (ie the number of rows affected).
1398  	 	 if (!mysqli_next_result($this->connection->_connectionID)) {
1399  	 	 	 return false;
1400  	 	 }
1401  
1402  	 	 // CD: There is no $this->_connectionID variable, at least in the ADO version I'm using
1403  	 	 $this->_queryID = ($ADODB_COUNTRECS) ? @mysqli_store_result($this->connection->_connectionID)
1404  	 	 	 : @mysqli_use_result($this->connection->_connectionID);
1405  
1406  	 	 if (!$this->_queryID) {
1407  	 	 	 return false;
1408  	 	 }
1409  
1410  	 	 $this->_inited     = false;
1411  	 	 $this->bind        = false;
1412  	 	 $this->_currentRow = -1;
1413  	 	 $this->init();
1414  	 	 return true;
1415  	 }
1416  
1417  	 /**
1418  	  * Moves the cursor to the next record of the recordset from the current position.
1419  	  *
1420  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:movenext
1421  	  *
1422  	  * @return bool False if there are no more records to move on to, otherwise true.
1423  	  */
1424  	function MoveNext()
1425  	 {
1426  	 	 if ($this->EOF) return false;
1427  	 	 $this->_currentRow++;
1428  	 	 $this->fields = @mysqli_fetch_array($this->_queryID,$this->fetchMode);
1429  
1430  	 	 if (is_array($this->fields)) {
1431  	 	 	 $this->_updatefields();
1432  	 	 	 return true;
1433  	 	 }
1434  	 	 $this->EOF = true;
1435  	 	 return false;
1436  	 }
1437  
1438  	 /**
1439  	  * Attempt to fetch a result row using the current fetch mode and return whether or not this was successful.
1440  	  *
1441  	  * @return bool True if row was fetched successfully, otherwise false.
1442  	  */
1443  	function _fetch()
1444  	 {
1445  	 	 $this->fields = mysqli_fetch_array($this->_queryID,$this->fetchMode);
1446  	 	 $this->_updatefields();
1447  	 	 return is_array($this->fields);
1448  	 }
1449  
1450  	 /**
1451  	  * Frees the memory associated with a result.
1452  	  *
1453  	  * @return void
1454  	  */
1455  	function _close()
1456  	 {
1457  	 	 //if results are attached to this pointer from Stored Procedure calls, the next standard query will die 2014
1458  	 	 //only a problem with persistent connections
1459  
1460  	 	 if (isset($this->connection->_connectionID) && $this->connection->_connectionID) {
1461  	 	 	 while (mysqli_more_results($this->connection->_connectionID)) {
1462  	 	 	 	 mysqli_next_result($this->connection->_connectionID);
1463  	 	 	 }
1464  	 	 }
1465  
1466  	 	 if ($this->_queryID instanceof mysqli_result) {
1467  	 	 	 mysqli_free_result($this->_queryID);
1468  	 	 }
1469  	 	 $this->_queryID = false;
1470  	 }
1471  
1472  /*
1473  
1474  0 = MYSQLI_TYPE_DECIMAL
1475  1 = MYSQLI_TYPE_CHAR
1476  1 = MYSQLI_TYPE_TINY
1477  2 = MYSQLI_TYPE_SHORT
1478  3 = MYSQLI_TYPE_LONG
1479  4 = MYSQLI_TYPE_FLOAT
1480  5 = MYSQLI_TYPE_DOUBLE
1481  6 = MYSQLI_TYPE_NULL
1482  7 = MYSQLI_TYPE_TIMESTAMP
1483  8 = MYSQLI_TYPE_LONGLONG
1484  9 = MYSQLI_TYPE_INT24
1485  10 = MYSQLI_TYPE_DATE
1486  11 = MYSQLI_TYPE_TIME
1487  12 = MYSQLI_TYPE_DATETIME
1488  13 = MYSQLI_TYPE_YEAR
1489  14 = MYSQLI_TYPE_NEWDATE
1490  247 = MYSQLI_TYPE_ENUM
1491  248 = MYSQLI_TYPE_SET
1492  249 = MYSQLI_TYPE_TINY_BLOB
1493  250 = MYSQLI_TYPE_MEDIUM_BLOB
1494  251 = MYSQLI_TYPE_LONG_BLOB
1495  252 = MYSQLI_TYPE_BLOB
1496  253 = MYSQLI_TYPE_VAR_STRING
1497  254 = MYSQLI_TYPE_STRING
1498  255 = MYSQLI_TYPE_GEOMETRY
1499  */
1500  
1501  	 /**
1502  	  * Get the MetaType character for a given field type.
1503  	  *
1504  	  * @param string|object $t The type to get the MetaType character for.
1505  	  * @param int $len (Optional) Redundant. Will always be set to -1.
1506  	  * @param bool|object $fieldobj (Optional)
1507  	  *
1508  	  * @return string The MetaType
1509  	  */
1510  	function MetaType($t, $len = -1, $fieldobj = false)
1511  	 {
1512  	 	 if (is_object($t)) {
1513  	 	 	 $fieldobj = $t;
1514  	 	 	 $t = $fieldobj->type;
1515  	 	 	 $len = $fieldobj->max_length;
1516  	 	 }
1517  
1518  	 	 $len = -1; // mysql max_length is not accurate
1519  	 	 switch (strtoupper($t)) {
1520  	 	 	 case 'STRING':
1521  	 	 	 case 'CHAR':
1522  	 	 	 case 'VARCHAR':
1523  	 	 	 case 'TINYBLOB':
1524  	 	 	 case 'TINYTEXT':
1525  	 	 	 case 'ENUM':
1526  	 	 	 case 'SET':
1527  
1528  	 	 	 case MYSQLI_TYPE_TINY_BLOB :
1529  //	 	 	 case MYSQLI_TYPE_CHAR :
1530  	 	 	 case MYSQLI_TYPE_STRING :
1531  	 	 	 case MYSQLI_TYPE_ENUM :
1532  	 	 	 case MYSQLI_TYPE_SET :
1533  	 	 	 case 253 :
1534  	 	 	 	 if ($len <= $this->blobSize) {
1535  	 	 	 	 	 return 'C';
1536  	 	 	 	 }
1537  
1538  	 	 	 case 'TEXT':
1539  	 	 	 case 'LONGTEXT':
1540  	 	 	 case 'MEDIUMTEXT':
1541  	 	 	 	 return 'X';
1542  
1543  	 	 	 // php_mysql extension always returns 'blob' even if 'text'
1544  	 	 	 // so we have to check whether binary...
1545  	 	 	 case 'IMAGE':
1546  	 	 	 case 'LONGBLOB':
1547  	 	 	 case 'BLOB':
1548  	 	 	 case 'MEDIUMBLOB':
1549  
1550  	 	 	 case MYSQLI_TYPE_BLOB :
1551  	 	 	 case MYSQLI_TYPE_LONG_BLOB :
1552  	 	 	 case MYSQLI_TYPE_MEDIUM_BLOB :
1553  	 	 	 	 return !empty($fieldobj->binary) ? 'B' : 'X';
1554  
1555  	 	 	 case 'YEAR':
1556  	 	 	 case 'DATE':
1557  	 	 	 case MYSQLI_TYPE_DATE :
1558  	 	 	 case MYSQLI_TYPE_YEAR :
1559  	 	 	 	 return 'D';
1560  
1561  	 	 	 case 'TIME':
1562  	 	 	 case 'DATETIME':
1563  	 	 	 case 'TIMESTAMP':
1564  
1565  	 	 	 case MYSQLI_TYPE_DATETIME :
1566  	 	 	 case MYSQLI_TYPE_NEWDATE :
1567  	 	 	 case MYSQLI_TYPE_TIME :
1568  	 	 	 case MYSQLI_TYPE_TIMESTAMP :
1569  	 	 	 	 return 'T';
1570  
1571  	 	 	 case 'INT':
1572  	 	 	 case 'INTEGER':
1573  	 	 	 case 'BIGINT':
1574  	 	 	 case 'TINYINT':
1575  	 	 	 case 'MEDIUMINT':
1576  	 	 	 case 'SMALLINT':
1577  
1578  	 	 	 case MYSQLI_TYPE_INT24 :
1579  	 	 	 case MYSQLI_TYPE_LONG :
1580  	 	 	 case MYSQLI_TYPE_LONGLONG :
1581  	 	 	 case MYSQLI_TYPE_SHORT :
1582  	 	 	 case MYSQLI_TYPE_TINY :
1583  	 	 	 	 if (!empty($fieldobj->primary_key)) {
1584  	 	 	 	 	 return 'R';
1585  	 	 	 	 }
1586  	 	 	 	 return 'I';
1587  
1588  	 	 	 // Added floating-point types
1589  	 	 	 // Maybe not necessary.
1590  	 	 	 case 'FLOAT':
1591  	 	 	 case 'DOUBLE':
1592  //	 	 	 case 'DOUBLE PRECISION':
1593  	 	 	 case 'DECIMAL':
1594  	 	 	 case 'DEC':
1595  	 	 	 case 'FIXED':
1596  	 	 	 default:
1597  	 	 	 	 //if (!is_numeric($t)) echo "<p>--- Error in type matching $t -----</p>";
1598  	 	 	 	 return 'N';
1599  	 	 }
1600  	 }
1601  
1602  
1603  } // rs class
1604  
1605  /**
1606   * Class ADORecordSet_array_mysqli
1607   */
1608  class ADORecordSet_array_mysqli extends ADORecordSet_array
1609  {
1610  	 /**
1611  	  * Get the MetaType character for a given field type.
1612  	  *
1613  	  * @param string|object $t The type to get the MetaType character for.
1614  	  * @param int $len (Optional) Redundant. Will always be set to -1.
1615  	  * @param bool|object $fieldobj (Optional)
1616  	  *
1617  	  * @return string The MetaType
1618  	  */
1619  	function MetaType($t, $len = -1, $fieldobj = false)
1620  	 {
1621  	 	 if (is_object($t)) {
1622  	 	 	 $fieldobj = $t;
1623  	 	 	 $t = $fieldobj->type;
1624  	 	 	 $len = $fieldobj->max_length;
1625  	 	 }
1626  
1627  	 	 $len = -1; // mysql max_length is not accurate
1628  	 	 switch (strtoupper($t)) {
1629  	 	 	 case 'STRING':
1630  	 	 	 case 'CHAR':
1631  	 	 	 case 'VARCHAR':
1632  	 	 	 case 'TINYBLOB':
1633  	 	 	 case 'TINYTEXT':
1634  	 	 	 case 'ENUM':
1635  	 	 	 case 'SET':
1636  
1637  	 	 	 case MYSQLI_TYPE_TINY_BLOB :
1638  //	 	 	 case MYSQLI_TYPE_CHAR :
1639  	 	 	 case MYSQLI_TYPE_STRING :
1640  	 	 	 case MYSQLI_TYPE_ENUM :
1641  	 	 	 case MYSQLI_TYPE_SET :
1642  	 	 	 case 253 :
1643  	 	 	 	 if ($len <= $this->blobSize) {
1644  	 	 	 	 	 return 'C';
1645  	 	 	 	 }
1646  
1647  	 	 	 case 'TEXT':
1648  	 	 	 case 'LONGTEXT':
1649  	 	 	 case 'MEDIUMTEXT':
1650  	 	 	 	 return 'X';
1651  
1652  	 	 	 // php_mysql extension always returns 'blob' even if 'text'
1653  	 	 	 // so we have to check whether binary...
1654  	 	 	 case 'IMAGE':
1655  	 	 	 case 'LONGBLOB':
1656  	 	 	 case 'BLOB':
1657  	 	 	 case 'MEDIUMBLOB':
1658  
1659  	 	 	 case MYSQLI_TYPE_BLOB :
1660  	 	 	 case MYSQLI_TYPE_LONG_BLOB :
1661  	 	 	 case MYSQLI_TYPE_MEDIUM_BLOB :
1662  	 	 	 	 return !empty($fieldobj->binary) ? 'B' : 'X';
1663  
1664  	 	 	 case 'YEAR':
1665  	 	 	 case 'DATE':
1666  	 	 	 case MYSQLI_TYPE_DATE :
1667  	 	 	 case MYSQLI_TYPE_YEAR :
1668  	 	 	 	 return 'D';
1669  
1670  	 	 	 case 'TIME':
1671  	 	 	 case 'DATETIME':
1672  	 	 	 case 'TIMESTAMP':
1673  
1674  	 	 	 case MYSQLI_TYPE_DATETIME :
1675  	 	 	 case MYSQLI_TYPE_NEWDATE :
1676  	 	 	 case MYSQLI_TYPE_TIME :
1677  	 	 	 case MYSQLI_TYPE_TIMESTAMP :
1678  	 	 	 	 return 'T';
1679  
1680  	 	 	 case 'INT':
1681  	 	 	 case 'INTEGER':
1682  	 	 	 case 'BIGINT':
1683  	 	 	 case 'TINYINT':
1684  	 	 	 case 'MEDIUMINT':
1685  	 	 	 case 'SMALLINT':
1686  
1687  	 	 	 case MYSQLI_TYPE_INT24 :
1688  	 	 	 case MYSQLI_TYPE_LONG :
1689  	 	 	 case MYSQLI_TYPE_LONGLONG :
1690  	 	 	 case MYSQLI_TYPE_SHORT :
1691  	 	 	 case MYSQLI_TYPE_TINY :
1692  	 	 	 	 if (!empty($fieldobj->primary_key)) {
1693  	 	 	 	 	 return 'R';
1694  	 	 	 	 }
1695  	 	 	 	 return 'I';
1696  
1697  	 	 	 // Added floating-point types
1698  	 	 	 // Maybe not necessary.
1699  	 	 	 case 'FLOAT':
1700  	 	 	 case 'DOUBLE':
1701  //	 	 	 case 'DOUBLE PRECISION':
1702  	 	 	 case 'DECIMAL':
1703  	 	 	 case 'DEC':
1704  	 	 	 case 'FIXED':
1705  	 	 	 default:
1706  	 	 	 	 //if (!is_numeric($t)) echo "<p>--- Error in type matching $t -----</p>";
1707  	 	 	 	 return 'N';
1708  	 	 }
1709  	 }
1710  }
1711  
1712  } // if defined _ADODB_MYSQLI_LAYER