PageRenderTime 62ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/adodb/drivers/adodb-mysqli.inc.php

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