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