mysqli.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2004 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license, |
  9. // | that is bundled with this package in the file LICENSE, and is |
  10. // | available at through the world-wide-web at |
  11. // | http://www.php.net/license/2_02.txt. |
  12. // | If you did not receive a copy of the PHP license and are unable to |
  13. // | obtain it through the world-wide-web, please send a note to |
  14. // | license@php.net so we can mail you a copy immediately. |
  15. // +----------------------------------------------------------------------+
  16. // | Author: Chaillan Nicolas <nicos@php.net> |
  17. // | Based on mysql.php by Stig Bakken <ssb@php.net> |
  18. // | Maintainer: Daniel Convissor <danielc@php.net> |
  19. // +----------------------------------------------------------------------+
  20. //
  21. // $Id: mysqli.php,v 1.3 2004/02/19 15:48:52 danielc Exp $
  22. // NOTE: The tableInfo() method must be redone because the functions it
  23. // relies on no longer exist in the new extension.
  24. //
  25. // EXPERIMENTAL
  26. require_once 'DB/common.php';
  27. /**
  28. * Database independent query interface definition for PHP's mysqli
  29. * extension.
  30. *
  31. * This is for MySQL versions 4.1 and above. Requires PHP 5.
  32. *
  33. * Note that persistent connections no longer exist.
  34. *
  35. * @package DB
  36. * @version $Id: mysqli.php,v 1.3 2004/02/19 15:48:52 danielc Exp $
  37. * @category Database
  38. * @author Chaillan Nicolas <nicos@php.net>
  39. */
  40. class DB_mysqli extends DB_common
  41. {
  42. // {{{ properties
  43. var $connection;
  44. var $phptype, $dbsyntax;
  45. var $prepare_tokens = array();
  46. var $prepare_types = array();
  47. var $num_rows = array();
  48. var $transaction_opcount = 0;
  49. var $autocommit = true;
  50. var $fetchmode = DB_FETCHMODE_ORDERED; /* Default fetch mode */
  51. var $_db = false;
  52. // }}}
  53. // {{{ constructor
  54. /**
  55. * DB_mysql constructor.
  56. *
  57. * @access public
  58. */
  59. function DB_mysqli()
  60. {
  61. $this->DB_common();
  62. $this->phptype = 'mysqli';
  63. $this->dbsyntax = 'mysqli';
  64. $this->features = array(
  65. 'prepare' => false,
  66. 'ssl' => true,
  67. 'transactions' => true,
  68. 'limit' => 'alter'
  69. );
  70. $this->errorcode_map = array(
  71. 1004 => DB_ERROR_CANNOT_CREATE,
  72. 1005 => DB_ERROR_CANNOT_CREATE,
  73. 1006 => DB_ERROR_CANNOT_CREATE,
  74. 1007 => DB_ERROR_ALREADY_EXISTS,
  75. 1008 => DB_ERROR_CANNOT_DROP,
  76. 1022 => DB_ERROR_ALREADY_EXISTS,
  77. 1046 => DB_ERROR_NODBSELECTED,
  78. 1050 => DB_ERROR_ALREADY_EXISTS,
  79. 1051 => DB_ERROR_NOSUCHTABLE,
  80. 1054 => DB_ERROR_NOSUCHFIELD,
  81. 1062 => DB_ERROR_ALREADY_EXISTS,
  82. 1064 => DB_ERROR_SYNTAX,
  83. 1100 => DB_ERROR_NOT_LOCKED,
  84. 1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
  85. 1146 => DB_ERROR_NOSUCHTABLE,
  86. 1048 => DB_ERROR_CONSTRAINT,
  87. 1216 => DB_ERROR_CONSTRAINT,
  88. );
  89. }
  90. // }}}
  91. // {{{ connect()
  92. /**
  93. * Connect to a database and log in as the specified user.
  94. *
  95. * @param string $dsn the data source name (see DB::parseDSN for syntax)
  96. * @param boolean $persistent (optional) whether the connection should
  97. * be persistent
  98. * @return mixed DB_OK on success, a DB error on failure
  99. * @access public
  100. */
  101. function connect($dsninfo, $persistent = false)
  102. {
  103. if (!DB::assertExtension('mysqli')) {
  104. return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
  105. }
  106. $this->dsn = $dsninfo;
  107. if ($dsninfo['protocol'] && $dsninfo['protocol'] == 'unix') {
  108. $dbhost = ':' . $dsninfo['socket'];
  109. } else {
  110. $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost';
  111. if ($dsninfo['port']) {
  112. $dbhost .= ':' . $dsninfo['port'];
  113. }
  114. }
  115. $ssl_mode = $this->getOption('ssl') === true ? 'CLIENT_SSL' : NULL;
  116. @ini_set('track_errors', true);
  117. if ($dbhost && $dsninfo['username'] && $dsninfo['password']) {
  118. // Need to verify if arguments are okay
  119. $conn = @mysqli_connect($dbhost, $dsninfo['username'],
  120. $dsninfo['password'], $ssl_mode);
  121. } elseif ($dbhost && isset($dsninfo['username'])) {
  122. $conn = @mysqli_connect($dbhost, $dsninfo['username'], NULL,
  123. $ssl_mode);
  124. } elseif ($dbhost) {
  125. $conn = @mysqli_connect($dbhost, NULL, NULL, $ssl_mode);
  126. } else {
  127. $conn = false;
  128. }
  129. @ini_restore('track_errors');
  130. if (!$conn) {
  131. if (($err = @mysqli_error()) != '') {
  132. return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null,
  133. null, $err);
  134. } elseif (empty($php_errormsg)) {
  135. return $this->raiseError(DB_ERROR_CONNECT_FAILED);
  136. } else {
  137. return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null,
  138. null, $php_errormsg);
  139. }
  140. }
  141. if ($dsninfo['database']) {
  142. if (!@mysqli_select_db($dsninfo['database'], $conn)) {
  143. switch(mysqli_errno($conn)) {
  144. case 1049:
  145. return $this->raiseError(DB_ERROR_NOSUCHDB, null, null,
  146. null, mysqli_error($conn));
  147. case 1044:
  148. return $this->raiseError(DB_ERROR_ACCESS_VIOLATION, null, null,
  149. null, mysqli_error($conn));
  150. default:
  151. return $this->raiseError(DB_ERROR, null, null,
  152. null, mysqli_error($conn));
  153. }
  154. }
  155. // fix to allow calls to different databases in the same script
  156. $this->_db = $dsninfo['database'];
  157. }
  158. if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
  159. $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT;
  160. $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL;
  161. $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT;
  162. }
  163. $this->connection = $conn;
  164. return DB_OK;
  165. }
  166. // }}}
  167. // {{{ disconnect()
  168. /**
  169. * Log out and disconnect from the database.
  170. *
  171. * @return boolean TRUE on success, FALSE if not connected
  172. * @access public
  173. */
  174. function disconnect()
  175. {
  176. $ret = mysqli_close($this->connection);
  177. $this->connection = null;
  178. return $ret;
  179. }
  180. // }}}
  181. // {{{ simpleQuery()
  182. /**
  183. * Send a query to MySQL and return the results as a MySQL resource
  184. * identifier.
  185. *
  186. * @param string $query the SQL query
  187. * @return mixed a valid MySQL result for successful SELECT
  188. * queries, DB_OK for other successful queries.
  189. * A DB error is returned on failure.
  190. * @access public
  191. */
  192. function simpleQuery($query)
  193. {
  194. $ismanip = DB::isManip($query);
  195. $this->last_query = $query;
  196. $query = $this->modifyQuery($query);
  197. if ($this->_db) {
  198. if (!@mysqli_select_db($this->_db, $this->connection)) {
  199. return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
  200. }
  201. }
  202. if (!$this->autocommit && $ismanip) {
  203. if ($this->transaction_opcount == 0) {
  204. $result = @mysqli_query('SET AUTOCOMMIT=0', $this->connection);
  205. $result = @mysqli_query('BEGIN', $this->connection);
  206. if (!$result) {
  207. return $this->mysqlRaiseError();
  208. }
  209. }
  210. $this->transaction_opcount++;
  211. }
  212. $result = @mysqli_query($query, $this->connection);
  213. if (!$result) {
  214. return $this->mysqlRaiseError();
  215. }
  216. if (is_resource($result)) {
  217. $numrows = $this->numrows($result);
  218. if (is_object($numrows)) {
  219. return $numrows;
  220. }
  221. $this->num_rows[(int)$result] = $numrows;
  222. return $result;
  223. }
  224. return DB_OK;
  225. }
  226. // }}}
  227. // {{{ nextResult()
  228. /**
  229. * Move the internal mysql result pointer to the next available result.
  230. *
  231. * This method has not been implemented yet.
  232. *
  233. * @param resource $result a valid sql result resource
  234. * @return false
  235. * @access public
  236. */
  237. function nextResult($result)
  238. {
  239. return false;
  240. }
  241. // }}}
  242. // {{{ fetchInto()
  243. /**
  244. * Fetch a row and insert the data into an existing array.
  245. *
  246. * Formating of the array and the data therein are configurable.
  247. * See DB_result::fetchInto() for more information.
  248. *
  249. * @param resource $result query result identifier
  250. * @param array $arr (reference) array where data from the row
  251. * should be placed
  252. * @param int $fetchmode how the resulting array should be indexed
  253. * @param int $rownum the row number to fetch
  254. *
  255. * @return mixed DB_OK on success, NULL when end of result set is
  256. * reached or on failure
  257. *
  258. * @see DB_result::fetchInto()
  259. * @access private
  260. */
  261. function fetchInto($result, &$arr, $fetchmode, $rownum=null)
  262. {
  263. if ($rownum !== null) {
  264. if (!@mysqli_data_seek($result, $rownum)) {
  265. return null;
  266. }
  267. }
  268. if ($fetchmode & DB_FETCHMODE_ASSOC) {
  269. $arr = @mysqli_fetch_array($result, MYSQLI_ASSOC);
  270. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
  271. $arr = array_change_key_case($arr, CASE_LOWER);
  272. }
  273. } else {
  274. $arr = @mysqli_fetch_row($result);
  275. }
  276. if (!$arr) {
  277. $errno = @mysqli_errno($this->connection);
  278. if (!$errno) {
  279. return NULL;
  280. }
  281. return $this->mysqlRaiseError($errno);
  282. }
  283. if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
  284. /*
  285. * Even though this DBMS already trims output, we do this because
  286. * a field might have intentional whitespace at the end that
  287. * gets removed by DB_PORTABILITY_RTRIM under another driver.
  288. */
  289. $this->_rtrimArrayValues($arr);
  290. }
  291. if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
  292. $this->_convertNullArrayValuesToEmpty($arr);
  293. }
  294. return DB_OK;
  295. }
  296. // }}}
  297. // {{{ freeResult()
  298. /**
  299. * Free the internal resources associated with $result.
  300. *
  301. * @param resource $result MySQL result identifier
  302. * @return bool TRUE on success, FALSE if $result is invalid
  303. * @access public
  304. */
  305. function freeResult($result)
  306. {
  307. unset($this->num_rows[(int)$result]);
  308. return @mysqli_free_result($result);
  309. }
  310. // }}}
  311. // {{{ numCols()
  312. /**
  313. * Get the number of columns in a result set.
  314. *
  315. * @param $result MySQL result identifier
  316. *
  317. * @access public
  318. *
  319. * @return int the number of columns per row in $result
  320. */
  321. function numCols($result)
  322. {
  323. $cols = @mysqli_num_fields($result);
  324. if (!$cols) {
  325. return $this->mysqlRaiseError();
  326. }
  327. return $cols;
  328. }
  329. // }}}
  330. // {{{ numRows()
  331. /**
  332. * Get the number of rows in a result set.
  333. *
  334. * @param resource $result MySQL result identifier
  335. * @return int the number of rows in $result
  336. * @access public
  337. */
  338. function numRows($result)
  339. {
  340. $rows = @mysqli_num_rows($result);
  341. if ($rows === null) {
  342. return $this->mysqlRaiseError();
  343. }
  344. return $rows;
  345. }
  346. // }}}
  347. // {{{ autoCommit()
  348. /**
  349. * Enable/disable automatic commits.
  350. */
  351. function autoCommit($onoff = false)
  352. {
  353. // XXX if $this->transaction_opcount > 0, we should probably
  354. // issue a warning here.
  355. $this->autocommit = $onoff ? true : false;
  356. return DB_OK;
  357. }
  358. // }}}
  359. // {{{ commit()
  360. /**
  361. * Commit the current transaction.
  362. */
  363. function commit()
  364. {
  365. if ($this->transaction_opcount > 0) {
  366. if ($this->_db) {
  367. if (!@mysqli_select_db($this->_db, $this->connection)) {
  368. return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
  369. }
  370. }
  371. $result = @mysqli_query('COMMIT', $this->connection);
  372. $result = @mysqli_query('SET AUTOCOMMIT=1', $this->connection);
  373. $this->transaction_opcount = 0;
  374. if (!$result) {
  375. return $this->mysqlRaiseError();
  376. }
  377. }
  378. return DB_OK;
  379. }
  380. // }}}
  381. // {{{ rollback()
  382. /**
  383. * Roll back (undo) the current transaction.
  384. */
  385. function rollback()
  386. {
  387. if ($this->transaction_opcount > 0) {
  388. if ($this->_db) {
  389. if (!@mysqli_select_db($this->_db, $this->connection)) {
  390. return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
  391. }
  392. }
  393. $result = @mysqli_query('ROLLBACK', $this->connection);
  394. $result = @mysqli_query('SET AUTOCOMMIT=1', $this->connection);
  395. $this->transaction_opcount = 0;
  396. if (!$result) {
  397. return $this->mysqlRaiseError();
  398. }
  399. }
  400. return DB_OK;
  401. }
  402. // }}}
  403. // {{{ affectedRows()
  404. /**
  405. * Gets the number of rows affected by the data manipulation
  406. * query. For other queries, this function returns 0.
  407. *
  408. * @return integer number of rows affected by the last query
  409. */
  410. function affectedRows()
  411. {
  412. if (DB::isManip($this->last_query)) {
  413. return @mysqli_affected_rows($this->connection);
  414. } else {
  415. return 0;
  416. }
  417. }
  418. // }}}
  419. // {{{ errorNative()
  420. /**
  421. * Get the native error code of the last error (if any) that
  422. * occured on the current connection.
  423. *
  424. * @return int native MySQL error code
  425. * @access public
  426. */
  427. function errorNative()
  428. {
  429. return mysqli_errno($this->connection);
  430. }
  431. // }}}
  432. // {{{ nextId()
  433. /**
  434. * Returns the next free id in a sequence
  435. *
  436. * @param string $seq_name name of the sequence
  437. * @param boolean $ondemand when true, the seqence is automatically
  438. * created if it does not exist
  439. *
  440. * @return int the next id number in the sequence. DB_Error if problem.
  441. *
  442. * @internal
  443. * @see DB_common::nextID()
  444. * @access public
  445. */
  446. function nextId($seq_name, $ondemand = true)
  447. {
  448. $seqname = $this->getSequenceName($seq_name);
  449. do {
  450. $repeat = 0;
  451. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  452. $result = $this->query("UPDATE ${seqname} ".
  453. 'SET id=LAST_INSERT_ID(id+1)');
  454. $this->popErrorHandling();
  455. if ($result == DB_OK) {
  456. /** COMMON CASE **/
  457. $id = @mysqli_insert_id($this->connection);
  458. if ($id != 0) {
  459. return $id;
  460. }
  461. /** EMPTY SEQ TABLE **/
  462. // Sequence table must be empty for some reason, so fill it and return 1
  463. // Obtain a user-level lock
  464. $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
  465. if (DB::isError($result)) {
  466. return $this->raiseError($result);
  467. }
  468. if ($result == 0) {
  469. // Failed to get the lock, bail with a DB_ERROR_NOT_LOCKED error
  470. return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
  471. }
  472. // add the default value
  473. $result = $this->query("REPLACE INTO ${seqname} VALUES (0)");
  474. if (DB::isError($result)) {
  475. return $this->raiseError($result);
  476. }
  477. // Release the lock
  478. $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
  479. if (DB::isError($result)) {
  480. return $this->raiseError($result);
  481. }
  482. // We know what the result will be, so no need to try again
  483. return 1;
  484. /** ONDEMAND TABLE CREATION **/
  485. } elseif ($ondemand && DB::isError($result) &&
  486. $result->getCode() == DB_ERROR_NOSUCHTABLE)
  487. {
  488. $result = $this->createSequence($seq_name);
  489. // Since createSequence initializes the ID to be 1,
  490. // we do not need to retrieve the ID again (or we will get 2)
  491. if (DB::isError($result)) {
  492. return $this->raiseError($result);
  493. } else {
  494. // First ID of a newly created sequence is 1
  495. return 1;
  496. }
  497. /** BACKWARDS COMPAT **/
  498. } elseif (DB::isError($result) &&
  499. $result->getCode() == DB_ERROR_ALREADY_EXISTS)
  500. {
  501. // see _BCsequence() comment
  502. $result = $this->_BCsequence($seqname);
  503. if (DB::isError($result)) {
  504. return $this->raiseError($result);
  505. }
  506. $repeat = 1;
  507. }
  508. } while ($repeat);
  509. return $this->raiseError($result);
  510. }
  511. /**
  512. * Creates a new sequence
  513. *
  514. * @param string $seq_name name of the new sequence
  515. *
  516. * @return int DB_OK on success. A DB_Error object is returned if
  517. * problems arise.
  518. *
  519. * @internal
  520. * @see DB_common::createSequence()
  521. * @access public
  522. */
  523. function createSequence($seq_name)
  524. {
  525. $seqname = $this->getSequenceName($seq_name);
  526. $res = $this->query("CREATE TABLE ${seqname} ".
  527. '(id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'.
  528. ' PRIMARY KEY(id))');
  529. if (DB::isError($res)) {
  530. return $res;
  531. }
  532. // insert yields value 1, nextId call will generate ID 2
  533. return $this->query("INSERT INTO ${seqname} VALUES(0)");
  534. }
  535. // }}}
  536. // {{{ dropSequence()
  537. /**
  538. * Deletes a sequence
  539. *
  540. * @param string $seq_name name of the sequence to be deleted
  541. *
  542. * @return int DB_OK on success. DB_Error if problems.
  543. *
  544. * @internal
  545. * @see DB_common::dropSequence()
  546. * @access public
  547. */
  548. function dropSequence($seq_name)
  549. {
  550. return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
  551. }
  552. // }}}
  553. // {{{ _BCsequence()
  554. /**
  555. * Backwards compatibility with old sequence emulation implementation
  556. * (clean up the dupes).
  557. *
  558. * @param string $seqname The sequence name to clean up
  559. * @return mixed DB_Error or true
  560. */
  561. function _BCsequence($seqname)
  562. {
  563. // Obtain a user-level lock... this will release any previous
  564. // application locks, but unlike LOCK TABLES, it does not abort
  565. // the current transaction and is much less frequently used.
  566. $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
  567. if (DB::isError($result)) {
  568. return $result;
  569. }
  570. if ($result == 0) {
  571. // Failed to get the lock, can't do the conversion, bail
  572. // with a DB_ERROR_NOT_LOCKED error
  573. return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
  574. }
  575. $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
  576. if (DB::isError($highest_id)) {
  577. return $highest_id;
  578. }
  579. // This should kill all rows except the highest
  580. // We should probably do something if $highest_id isn't
  581. // numeric, but I'm at a loss as how to handle that...
  582. $result = $this->query("DELETE FROM ${seqname} WHERE id <> $highest_id");
  583. if (DB::isError($result)) {
  584. return $result;
  585. }
  586. // If another thread has been waiting for this lock,
  587. // it will go thru the above procedure, but will have no
  588. // real effect
  589. $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
  590. if (DB::isError($result)) {
  591. return $result;
  592. }
  593. return true;
  594. }
  595. // }}}
  596. // {{{ quoteIdentifier()
  597. /**
  598. * Quote a string so it can be safely used as a table or column name
  599. *
  600. * Quoting style depends on which database driver is being used.
  601. *
  602. * MySQL can't handle the backtick character (<kbd>`</kbd>) in
  603. * table or column names.
  604. *
  605. * @param string $str identifier name to be quoted
  606. *
  607. * @return string quoted identifier string
  608. *
  609. * @since 1.6.0
  610. * @access public
  611. * @internal
  612. */
  613. function quoteIdentifier($str)
  614. {
  615. return '`' . $str . '`';
  616. }
  617. // }}}
  618. // {{{ escapeSimple()
  619. /**
  620. * Escape a string according to the current DBMS's standards
  621. *
  622. * @param string $str the string to be escaped
  623. *
  624. * @return string the escaped string
  625. *
  626. * @internal
  627. */
  628. function escapeSimple($str) {
  629. return mysqli_real_escape_string($str, $this->connection);
  630. }
  631. // }}}
  632. // {{{ modifyQuery()
  633. function modifyQuery($query)
  634. {
  635. if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) {
  636. // "DELETE FROM table" gives 0 affected rows in MySQL.
  637. // This little hack lets you know how many rows were deleted.
  638. if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
  639. $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
  640. 'DELETE FROM \1 WHERE 1=1', $query);
  641. }
  642. }
  643. return $query;
  644. }
  645. // }}}
  646. // {{{ modifyLimitQuery()
  647. function modifyLimitQuery($query, $from, $count)
  648. {
  649. if (DB::isManip($query)) {
  650. return $query . " LIMIT $count";
  651. } else {
  652. return $query . " LIMIT $from, $count";
  653. }
  654. }
  655. // }}}
  656. // {{{ mysqlRaiseError()
  657. /**
  658. * Gather information about an error, then use that info to create a
  659. * DB error object and finally return that object.
  660. *
  661. * @param integer $errno PEAR error number (usually a DB constant) if
  662. * manually raising an error
  663. * @return object DB error object
  664. * @see DB_common::errorCode()
  665. * @see DB_common::raiseError()
  666. */
  667. function mysqlRaiseError($errno = null)
  668. {
  669. if ($errno === null) {
  670. $errno = $this->errorCode(mysqli_errno($this->connection));
  671. }
  672. return $this->raiseError($errno, null, null, null,
  673. @mysqli_errno($this->connection) . ' ** ' .
  674. @mysqli_error($this->connection));
  675. }
  676. // }}}
  677. // {{{ tableInfo()
  678. /**
  679. * Returns information about a table or a result set.
  680. *
  681. * WARNING: this method will probably not work because the mysqli_*()
  682. * functions it relies upon may not exist.
  683. *
  684. * @param object|string $result DB_result object from a query or a
  685. * string containing the name of a table
  686. * @param int $mode a valid tableInfo mode
  687. * @return array an associative array with the information requested
  688. * or an error object if something is wrong
  689. * @access public
  690. * @internal
  691. * @see DB_common::tableInfo()
  692. */
  693. function tableInfo($result, $mode = null) {
  694. if (isset($result->result)) {
  695. /*
  696. * Probably received a result object.
  697. * Extract the result resource identifier.
  698. */
  699. $id = $result->result;
  700. $got_string = false;
  701. } elseif (is_string($result)) {
  702. /*
  703. * Probably received a table name.
  704. * Create a result resource identifier.
  705. */
  706. $id = @mysqli_list_fields($this->dsn['database'],
  707. $result, $this->connection);
  708. $got_string = true;
  709. } else {
  710. /*
  711. * Probably received a result resource identifier.
  712. * Copy it.
  713. * Depricated. Here for compatibility only.
  714. */
  715. $id = $result;
  716. $got_string = false;
  717. }
  718. if (!is_resource($id)) {
  719. return $this->mysqlRaiseError(DB_ERROR_NEED_MORE_DATA);
  720. }
  721. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
  722. $case_func = 'strtolower';
  723. } else {
  724. $case_func = 'strval';
  725. }
  726. $count = @mysqli_num_fields($id);
  727. // made this IF due to performance (one if is faster than $count if's)
  728. if (!$mode) {
  729. for ($i=0; $i<$count; $i++) {
  730. $res[$i]['table'] = $case_func(@mysqli_field_table($id, $i));
  731. $res[$i]['name'] = $case_func(@mysqli_field_name($id, $i));
  732. $res[$i]['type'] = @mysqli_field_type($id, $i);
  733. $res[$i]['len'] = @mysqli_field_len($id, $i);
  734. $res[$i]['flags'] = @mysqli_field_flags($id, $i);
  735. }
  736. } else { // full
  737. $res['num_fields']= $count;
  738. for ($i=0; $i<$count; $i++) {
  739. $res[$i]['table'] = $case_func(@mysqli_field_table($id, $i));
  740. $res[$i]['name'] = $case_func(@mysqli_field_name($id, $i));
  741. $res[$i]['type'] = @mysqli_field_type($id, $i);
  742. $res[$i]['len'] = @mysqli_field_len($id, $i);
  743. $res[$i]['flags'] = @mysqli_field_flags($id, $i);
  744. if ($mode & DB_TABLEINFO_ORDER) {
  745. $res['order'][$res[$i]['name']] = $i;
  746. }
  747. if ($mode & DB_TABLEINFO_ORDERTABLE) {
  748. $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  749. }
  750. }
  751. }
  752. // free the result only if we were called on a table
  753. if ($got_string) {
  754. @mysqli_free_result($id);
  755. }
  756. return $res;
  757. }
  758. // }}}
  759. // {{{ getSpecialQuery()
  760. /**
  761. * Returns the query needed to get some backend info.
  762. *
  763. * @param string $type What kind of info you want to retrieve
  764. * @return string The SQL query string
  765. */
  766. function getSpecialQuery($type)
  767. {
  768. switch ($type) {
  769. case 'tables':
  770. return 'SHOW TABLES';
  771. case 'views':
  772. return DB_ERROR_NOT_CAPABLE;
  773. case 'users':
  774. $sql = 'select distinct User from user';
  775. if($this->dsn['database'] != 'mysql') {
  776. $dsn = $this->dsn;
  777. $dsn['database'] = 'mysql';
  778. if (DB::isError($db = DB::connect($dsn))) {
  779. return $db;
  780. }
  781. $sql = $db->getCol($sql);
  782. $db->disconnect();
  783. // XXX Fixme the mysql driver should take care of this
  784. if (!@mysqli_select_db($this->dsn['database'], $this->connection)) {
  785. return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
  786. }
  787. }
  788. return $sql;
  789. case 'databases':
  790. return 'SHOW DATABASES';
  791. default:
  792. return null;
  793. }
  794. }
  795. // }}}
  796. }
  797. /*
  798. * Local variables:
  799. * tab-width: 4
  800. * c-basic-offset: 4
  801. * End:
  802. */
  803. ?>