mysqli.php 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * The PEAR DB driver for PHP's mysqli extension
  5. * for interacting with MySQL databases
  6. *
  7. * PHP version 5
  8. *
  9. * LICENSE: This source file is subject to version 3.0 of the PHP license
  10. * that is available through the world-wide-web at the following URI:
  11. * http://www.php.net/license/3_0.txt. If you did not receive a copy of
  12. * the PHP License and are unable to obtain it through the web, please
  13. * send a note to license@php.net so we can mail you a copy immediately.
  14. *
  15. * @category Database
  16. * @package DB
  17. * @author Daniel Convissor <danielc@php.net>
  18. * @copyright 1997-2007 The PHP Group
  19. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  20. * @version CVS: $Id$
  21. * @link http://pear.php.net/package/DB
  22. */
  23. /**
  24. * Obtain the DB_common class so it can be extended from
  25. */
  26. require_once 'DB/common.php';
  27. /**
  28. * The methods PEAR DB uses to interact with PHP's mysqli extension
  29. * for interacting with MySQL databases
  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. * These methods overload the ones declared in DB_common.
  36. *
  37. * @category Database
  38. * @package DB
  39. * @author Daniel Convissor <danielc@php.net>
  40. * @copyright 1997-2007 The PHP Group
  41. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  42. * @version Release: 1.8.2
  43. * @link http://pear.php.net/package/DB
  44. * @since Class functional since Release 1.6.3
  45. */
  46. class DB_mysqli extends DB_common
  47. {
  48. // {{{ properties
  49. /**
  50. * The DB driver type (mysql, oci8, odbc, etc.)
  51. * @var string
  52. */
  53. var $phptype = 'mysqli';
  54. /**
  55. * The database syntax variant to be used (db2, access, etc.), if any
  56. * @var string
  57. */
  58. var $dbsyntax = 'mysqli';
  59. /**
  60. * The capabilities of this DB implementation
  61. *
  62. * The 'new_link' element contains the PHP version that first provided
  63. * new_link support for this DBMS. Contains false if it's unsupported.
  64. *
  65. * Meaning of the 'limit' element:
  66. * + 'emulate' = emulate with fetch row by number
  67. * + 'alter' = alter the query
  68. * + false = skip rows
  69. *
  70. * @var array
  71. */
  72. var $features = array(
  73. 'limit' => 'alter',
  74. 'new_link' => false,
  75. 'numrows' => true,
  76. 'pconnect' => false,
  77. 'prepare' => false,
  78. 'ssl' => true,
  79. 'transactions' => true,
  80. );
  81. /**
  82. * A mapping of native error codes to DB error codes
  83. * @var array
  84. */
  85. var $errorcode_map = array(
  86. 1004 => DB_ERROR_CANNOT_CREATE,
  87. 1005 => DB_ERROR_CANNOT_CREATE,
  88. 1006 => DB_ERROR_CANNOT_CREATE,
  89. 1007 => DB_ERROR_ALREADY_EXISTS,
  90. 1008 => DB_ERROR_CANNOT_DROP,
  91. 1022 => DB_ERROR_ALREADY_EXISTS,
  92. 1044 => DB_ERROR_ACCESS_VIOLATION,
  93. 1046 => DB_ERROR_NODBSELECTED,
  94. 1048 => DB_ERROR_CONSTRAINT,
  95. 1049 => DB_ERROR_NOSUCHDB,
  96. 1050 => DB_ERROR_ALREADY_EXISTS,
  97. 1051 => DB_ERROR_NOSUCHTABLE,
  98. 1054 => DB_ERROR_NOSUCHFIELD,
  99. 1061 => DB_ERROR_ALREADY_EXISTS,
  100. 1062 => DB_ERROR_ALREADY_EXISTS,
  101. 1064 => DB_ERROR_SYNTAX,
  102. 1091 => DB_ERROR_NOT_FOUND,
  103. 1100 => DB_ERROR_NOT_LOCKED,
  104. 1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
  105. 1142 => DB_ERROR_ACCESS_VIOLATION,
  106. 1146 => DB_ERROR_NOSUCHTABLE,
  107. 1216 => DB_ERROR_CONSTRAINT,
  108. 1217 => DB_ERROR_CONSTRAINT,
  109. 1356 => DB_ERROR_DIVZERO,
  110. 1451 => DB_ERROR_CONSTRAINT,
  111. 1452 => DB_ERROR_CONSTRAINT,
  112. );
  113. /**
  114. * The raw database connection created by PHP
  115. * @var resource
  116. */
  117. var $connection;
  118. /**
  119. * The DSN information for connecting to a database
  120. * @var array
  121. */
  122. var $dsn = array();
  123. /**
  124. * Should data manipulation queries be committed automatically?
  125. * @var bool
  126. * @access private
  127. */
  128. var $autocommit = true;
  129. /**
  130. * The quantity of transactions begun
  131. *
  132. * {@internal While this is private, it can't actually be designated
  133. * private in PHP 5 because it is directly accessed in the test suite.}}
  134. *
  135. * @var integer
  136. * @access private
  137. */
  138. var $transaction_opcount = 0;
  139. /**
  140. * The database specified in the DSN
  141. *
  142. * It's a fix to allow calls to different databases in the same script.
  143. *
  144. * @var string
  145. * @access private
  146. */
  147. var $_db = '';
  148. /**
  149. * Array for converting MYSQLI_*_FLAG constants to text values
  150. * @var array
  151. * @access public
  152. * @since Property available since Release 1.6.5
  153. */
  154. var $mysqli_flags = array(
  155. MYSQLI_NOT_NULL_FLAG => 'not_null',
  156. MYSQLI_PRI_KEY_FLAG => 'primary_key',
  157. MYSQLI_UNIQUE_KEY_FLAG => 'unique_key',
  158. MYSQLI_MULTIPLE_KEY_FLAG => 'multiple_key',
  159. MYSQLI_BLOB_FLAG => 'blob',
  160. MYSQLI_UNSIGNED_FLAG => 'unsigned',
  161. MYSQLI_ZEROFILL_FLAG => 'zerofill',
  162. MYSQLI_AUTO_INCREMENT_FLAG => 'auto_increment',
  163. MYSQLI_TIMESTAMP_FLAG => 'timestamp',
  164. MYSQLI_SET_FLAG => 'set',
  165. // MYSQLI_NUM_FLAG => 'numeric', // unnecessary
  166. // MYSQLI_PART_KEY_FLAG => 'multiple_key', // duplicatvie
  167. MYSQLI_GROUP_FLAG => 'group_by'
  168. );
  169. /**
  170. * Array for converting MYSQLI_TYPE_* constants to text values
  171. * @var array
  172. * @access public
  173. * @since Property available since Release 1.6.5
  174. */
  175. var $mysqli_types = array(
  176. MYSQLI_TYPE_DECIMAL => 'decimal',
  177. MYSQLI_TYPE_TINY => 'tinyint',
  178. MYSQLI_TYPE_SHORT => 'int',
  179. MYSQLI_TYPE_LONG => 'int',
  180. MYSQLI_TYPE_FLOAT => 'float',
  181. MYSQLI_TYPE_DOUBLE => 'double',
  182. // MYSQLI_TYPE_NULL => 'DEFAULT NULL', // let flags handle it
  183. MYSQLI_TYPE_TIMESTAMP => 'timestamp',
  184. MYSQLI_TYPE_LONGLONG => 'bigint',
  185. MYSQLI_TYPE_INT24 => 'mediumint',
  186. MYSQLI_TYPE_DATE => 'date',
  187. MYSQLI_TYPE_TIME => 'time',
  188. MYSQLI_TYPE_DATETIME => 'datetime',
  189. MYSQLI_TYPE_YEAR => 'year',
  190. MYSQLI_TYPE_NEWDATE => 'date',
  191. MYSQLI_TYPE_ENUM => 'enum',
  192. MYSQLI_TYPE_SET => 'set',
  193. MYSQLI_TYPE_TINY_BLOB => 'tinyblob',
  194. MYSQLI_TYPE_MEDIUM_BLOB => 'mediumblob',
  195. MYSQLI_TYPE_LONG_BLOB => 'longblob',
  196. MYSQLI_TYPE_BLOB => 'blob',
  197. MYSQLI_TYPE_VAR_STRING => 'varchar',
  198. MYSQLI_TYPE_STRING => 'char',
  199. MYSQLI_TYPE_GEOMETRY => 'geometry',
  200. /* These constants are conditionally compiled in ext/mysqli, so we'll
  201. * define them by number rather than constant. */
  202. 16 => 'bit',
  203. 246 => 'decimal',
  204. );
  205. // }}}
  206. // {{{ constructor
  207. /**
  208. * This constructor calls <kbd>$this->DB_common()</kbd>
  209. *
  210. * @return void
  211. */
  212. function DB_mysqli()
  213. {
  214. $this->DB_common();
  215. }
  216. // }}}
  217. // {{{ connect()
  218. /**
  219. * Connect to the database server, log in and open the database
  220. *
  221. * Don't call this method directly. Use DB::connect() instead.
  222. *
  223. * PEAR DB's mysqli driver supports the following extra DSN options:
  224. * + When the 'ssl' $option passed to DB::connect() is true:
  225. * + key The path to the key file.
  226. * + cert The path to the certificate file.
  227. * + ca The path to the certificate authority file.
  228. * + capath The path to a directory that contains trusted SSL
  229. * CA certificates in pem format.
  230. * + cipher The list of allowable ciphers for SSL encryption.
  231. *
  232. * Example of how to connect using SSL:
  233. * <code>
  234. * require_once 'DB.php';
  235. *
  236. * $dsn = array(
  237. * 'phptype' => 'mysqli',
  238. * 'username' => 'someuser',
  239. * 'password' => 'apasswd',
  240. * 'hostspec' => 'localhost',
  241. * 'database' => 'thedb',
  242. * 'key' => 'client-key.pem',
  243. * 'cert' => 'client-cert.pem',
  244. * 'ca' => 'cacert.pem',
  245. * 'capath' => '/path/to/ca/dir',
  246. * 'cipher' => 'AES',
  247. * );
  248. *
  249. * $options = array(
  250. * 'ssl' => true,
  251. * );
  252. *
  253. * $db = DB::connect($dsn, $options);
  254. * if (PEAR::isError($db)) {
  255. * die($db->getMessage());
  256. * }
  257. * </code>
  258. *
  259. * @param array $dsn the data source name
  260. * @param bool $persistent should the connection be persistent?
  261. *
  262. * @return int DB_OK on success. A DB_Error object on failure.
  263. */
  264. function connect($dsn, $persistent = false)
  265. {
  266. if (!PEAR::loadExtension('mysqli')) {
  267. return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
  268. }
  269. $this->dsn = $dsn;
  270. if ($dsn['dbsyntax']) {
  271. $this->dbsyntax = $dsn['dbsyntax'];
  272. }
  273. $ini = ini_get('track_errors');
  274. @ini_set('track_errors', 1);
  275. $php_errormsg = '';
  276. if (((int) $this->getOption('ssl')) === 1) {
  277. $init = mysqli_init();
  278. mysqli_ssl_set(
  279. $init,
  280. empty($dsn['key']) ? null : $dsn['key'],
  281. empty($dsn['cert']) ? null : $dsn['cert'],
  282. empty($dsn['ca']) ? null : $dsn['ca'],
  283. empty($dsn['capath']) ? null : $dsn['capath'],
  284. empty($dsn['cipher']) ? null : $dsn['cipher']
  285. );
  286. if ($this->connection = @mysqli_real_connect(
  287. $init,
  288. $dsn['hostspec'],
  289. $dsn['username'],
  290. $dsn['password'],
  291. $dsn['database'],
  292. $dsn['port'],
  293. $dsn['socket']))
  294. {
  295. $this->connection = $init;
  296. }
  297. } else {
  298. $this->connection = @mysqli_connect(
  299. $dsn['hostspec'],
  300. $dsn['username'],
  301. $dsn['password'],
  302. $dsn['database'],
  303. $dsn['port'],
  304. $dsn['socket']
  305. );
  306. }
  307. @ini_set('track_errors', $ini);
  308. if (!$this->connection) {
  309. if (($err = @mysqli_connect_error()) != '') {
  310. return $this->raiseError(DB_ERROR_CONNECT_FAILED,
  311. null, null, null,
  312. $err);
  313. } else {
  314. return $this->raiseError(DB_ERROR_CONNECT_FAILED,
  315. null, null, null,
  316. $php_errormsg);
  317. }
  318. }
  319. if ($dsn['database']) {
  320. $this->_db = $dsn['database'];
  321. }
  322. return DB_OK;
  323. }
  324. // }}}
  325. // {{{ disconnect()
  326. /**
  327. * Disconnects from the database server
  328. *
  329. * @return bool TRUE on success, FALSE on failure
  330. */
  331. function disconnect()
  332. {
  333. $ret = @mysqli_close($this->connection);
  334. $this->connection = null;
  335. return $ret;
  336. }
  337. // }}}
  338. // {{{ simpleQuery()
  339. /**
  340. * Sends a query to the database server
  341. *
  342. * @param string the SQL query string
  343. *
  344. * @return mixed + a PHP result resrouce for successful SELECT queries
  345. * + the DB_OK constant for other successful queries
  346. * + a DB_Error object on failure
  347. */
  348. function simpleQuery($query)
  349. {
  350. $ismanip = $this->_checkManip($query);
  351. $this->last_query = $query;
  352. $query = $this->modifyQuery($query);
  353. if ($this->_db) {
  354. if (!@mysqli_select_db($this->connection, $this->_db)) {
  355. return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
  356. }
  357. }
  358. if (!$this->autocommit && $ismanip) {
  359. if ($this->transaction_opcount == 0) {
  360. $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=0');
  361. $result = @mysqli_query($this->connection, 'BEGIN');
  362. if (!$result) {
  363. return $this->mysqliRaiseError();
  364. }
  365. }
  366. $this->transaction_opcount++;
  367. }
  368. $result = @mysqli_query($this->connection, $query);
  369. if (!$result) {
  370. return $this->mysqliRaiseError();
  371. }
  372. if (is_object($result)) {
  373. return $result;
  374. }
  375. return DB_OK;
  376. }
  377. // }}}
  378. // {{{ nextResult()
  379. /**
  380. * Move the internal mysql result pointer to the next available result.
  381. *
  382. * This method has not been implemented yet.
  383. *
  384. * @param resource $result a valid sql result resource
  385. * @return false
  386. * @access public
  387. */
  388. function nextResult($result)
  389. {
  390. return false;
  391. }
  392. // }}}
  393. // {{{ fetchInto()
  394. /**
  395. * Places a row from the result set into the given array
  396. *
  397. * Formating of the array and the data therein are configurable.
  398. * See DB_result::fetchInto() for more information.
  399. *
  400. * This method is not meant to be called directly. Use
  401. * DB_result::fetchInto() instead. It can't be declared "protected"
  402. * because DB_result is a separate object.
  403. *
  404. * @param resource $result the query result resource
  405. * @param array $arr the referenced array to put the data in
  406. * @param int $fetchmode how the resulting array should be indexed
  407. * @param int $rownum the row number to fetch (0 = first row)
  408. *
  409. * @return mixed DB_OK on success, NULL when the end of a result set is
  410. * reached or on failure
  411. *
  412. * @see DB_result::fetchInto()
  413. */
  414. function fetchInto($result, &$arr, $fetchmode, $rownum = null)
  415. {
  416. if ($rownum !== null) {
  417. if (!@mysqli_data_seek($result, $rownum)) {
  418. return null;
  419. }
  420. }
  421. if ($fetchmode & DB_FETCHMODE_ASSOC) {
  422. $arr = @mysqli_fetch_array($result, MYSQLI_ASSOC);
  423. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
  424. $arr = array_change_key_case($arr, CASE_LOWER);
  425. }
  426. } else {
  427. $arr = @mysqli_fetch_row($result);
  428. }
  429. if (!$arr) {
  430. return null;
  431. }
  432. if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
  433. /*
  434. * Even though this DBMS already trims output, we do this because
  435. * a field might have intentional whitespace at the end that
  436. * gets removed by DB_PORTABILITY_RTRIM under another driver.
  437. */
  438. $this->_rtrimArrayValues($arr);
  439. }
  440. if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
  441. $this->_convertNullArrayValuesToEmpty($arr);
  442. }
  443. return DB_OK;
  444. }
  445. // }}}
  446. // {{{ freeResult()
  447. /**
  448. * Deletes the result set and frees the memory occupied by the result set
  449. *
  450. * This method is not meant to be called directly. Use
  451. * DB_result::free() instead. It can't be declared "protected"
  452. * because DB_result is a separate object.
  453. *
  454. * @param resource $result PHP's query result resource
  455. *
  456. * @return bool TRUE on success, FALSE if $result is invalid
  457. *
  458. * @see DB_result::free()
  459. */
  460. function freeResult($result)
  461. {
  462. return is_resource($result) ? mysqli_free_result($result) : false;
  463. }
  464. // }}}
  465. // {{{ numCols()
  466. /**
  467. * Gets the number of columns in a result set
  468. *
  469. * This method is not meant to be called directly. Use
  470. * DB_result::numCols() instead. It can't be declared "protected"
  471. * because DB_result is a separate object.
  472. *
  473. * @param resource $result PHP's query result resource
  474. *
  475. * @return int the number of columns. A DB_Error object on failure.
  476. *
  477. * @see DB_result::numCols()
  478. */
  479. function numCols($result)
  480. {
  481. $cols = @mysqli_num_fields($result);
  482. if (!$cols) {
  483. return $this->mysqliRaiseError();
  484. }
  485. return $cols;
  486. }
  487. // }}}
  488. // {{{ numRows()
  489. /**
  490. * Gets the number of rows in a result set
  491. *
  492. * This method is not meant to be called directly. Use
  493. * DB_result::numRows() instead. It can't be declared "protected"
  494. * because DB_result is a separate object.
  495. *
  496. * @param resource $result PHP's query result resource
  497. *
  498. * @return int the number of rows. A DB_Error object on failure.
  499. *
  500. * @see DB_result::numRows()
  501. */
  502. function numRows($result)
  503. {
  504. $rows = @mysqli_num_rows($result);
  505. if ($rows === null) {
  506. return $this->mysqliRaiseError();
  507. }
  508. return $rows;
  509. }
  510. // }}}
  511. // {{{ autoCommit()
  512. /**
  513. * Enables or disables automatic commits
  514. *
  515. * @param bool $onoff true turns it on, false turns it off
  516. *
  517. * @return int DB_OK on success. A DB_Error object if the driver
  518. * doesn't support auto-committing transactions.
  519. */
  520. function autoCommit($onoff = false)
  521. {
  522. // XXX if $this->transaction_opcount > 0, we should probably
  523. // issue a warning here.
  524. $this->autocommit = $onoff ? true : false;
  525. return DB_OK;
  526. }
  527. // }}}
  528. // {{{ commit()
  529. /**
  530. * Commits the current transaction
  531. *
  532. * @return int DB_OK on success. A DB_Error object on failure.
  533. */
  534. function commit()
  535. {
  536. if ($this->transaction_opcount > 0) {
  537. if ($this->_db) {
  538. if (!@mysqli_select_db($this->connection, $this->_db)) {
  539. return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
  540. }
  541. }
  542. $result = @mysqli_query($this->connection, 'COMMIT');
  543. $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1');
  544. $this->transaction_opcount = 0;
  545. if (!$result) {
  546. return $this->mysqliRaiseError();
  547. }
  548. }
  549. return DB_OK;
  550. }
  551. // }}}
  552. // {{{ rollback()
  553. /**
  554. * Reverts the current transaction
  555. *
  556. * @return int DB_OK on success. A DB_Error object on failure.
  557. */
  558. function rollback()
  559. {
  560. if ($this->transaction_opcount > 0) {
  561. if ($this->_db) {
  562. if (!@mysqli_select_db($this->connection, $this->_db)) {
  563. return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
  564. }
  565. }
  566. $result = @mysqli_query($this->connection, 'ROLLBACK');
  567. $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1');
  568. $this->transaction_opcount = 0;
  569. if (!$result) {
  570. return $this->mysqliRaiseError();
  571. }
  572. }
  573. return DB_OK;
  574. }
  575. // }}}
  576. // {{{ affectedRows()
  577. /**
  578. * Determines the number of rows affected by a data maniuplation query
  579. *
  580. * 0 is returned for queries that don't manipulate data.
  581. *
  582. * @return int the number of rows. A DB_Error object on failure.
  583. */
  584. function affectedRows()
  585. {
  586. if ($this->_last_query_manip) {
  587. return @mysqli_affected_rows($this->connection);
  588. } else {
  589. return 0;
  590. }
  591. }
  592. // }}}
  593. // {{{ nextId()
  594. /**
  595. * Returns the next free id in a sequence
  596. *
  597. * @param string $seq_name name of the sequence
  598. * @param boolean $ondemand when true, the seqence is automatically
  599. * created if it does not exist
  600. *
  601. * @return int the next id number in the sequence.
  602. * A DB_Error object on failure.
  603. *
  604. * @see DB_common::nextID(), DB_common::getSequenceName(),
  605. * DB_mysqli::createSequence(), DB_mysqli::dropSequence()
  606. */
  607. function nextId($seq_name, $ondemand = true)
  608. {
  609. $seqname = $this->getSequenceName($seq_name);
  610. do {
  611. $repeat = 0;
  612. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  613. $result = $this->query('UPDATE ' . $seqname
  614. . ' SET id = LAST_INSERT_ID(id + 1)');
  615. $this->popErrorHandling();
  616. if ($result === DB_OK) {
  617. // COMMON CASE
  618. $id = @mysqli_insert_id($this->connection);
  619. if ($id != 0) {
  620. return $id;
  621. }
  622. // EMPTY SEQ TABLE
  623. // Sequence table must be empty for some reason,
  624. // so fill it and return 1
  625. // Obtain a user-level lock
  626. $result = $this->getOne('SELECT GET_LOCK('
  627. . "'${seqname}_lock', 10)");
  628. if (DB::isError($result)) {
  629. return $this->raiseError($result);
  630. }
  631. if ($result == 0) {
  632. return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED);
  633. }
  634. // add the default value
  635. $result = $this->query('REPLACE INTO ' . $seqname
  636. . ' (id) VALUES (0)');
  637. if (DB::isError($result)) {
  638. return $this->raiseError($result);
  639. }
  640. // Release the lock
  641. $result = $this->getOne('SELECT RELEASE_LOCK('
  642. . "'${seqname}_lock')");
  643. if (DB::isError($result)) {
  644. return $this->raiseError($result);
  645. }
  646. // We know what the result will be, so no need to try again
  647. return 1;
  648. } elseif ($ondemand && DB::isError($result) &&
  649. $result->getCode() == DB_ERROR_NOSUCHTABLE)
  650. {
  651. // ONDEMAND TABLE CREATION
  652. $result = $this->createSequence($seq_name);
  653. // Since createSequence initializes the ID to be 1,
  654. // we do not need to retrieve the ID again (or we will get 2)
  655. if (DB::isError($result)) {
  656. return $this->raiseError($result);
  657. } else {
  658. // First ID of a newly created sequence is 1
  659. return 1;
  660. }
  661. } elseif (DB::isError($result) &&
  662. $result->getCode() == DB_ERROR_ALREADY_EXISTS)
  663. {
  664. // BACKWARDS COMPAT
  665. // see _BCsequence() comment
  666. $result = $this->_BCsequence($seqname);
  667. if (DB::isError($result)) {
  668. return $this->raiseError($result);
  669. }
  670. $repeat = 1;
  671. }
  672. } while ($repeat);
  673. return $this->raiseError($result);
  674. }
  675. /**
  676. * Creates a new sequence
  677. *
  678. * @param string $seq_name name of the new sequence
  679. *
  680. * @return int DB_OK on success. A DB_Error object on failure.
  681. *
  682. * @see DB_common::createSequence(), DB_common::getSequenceName(),
  683. * DB_mysqli::nextID(), DB_mysqli::dropSequence()
  684. */
  685. function createSequence($seq_name)
  686. {
  687. $seqname = $this->getSequenceName($seq_name);
  688. $res = $this->query('CREATE TABLE ' . $seqname
  689. . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'
  690. . ' PRIMARY KEY(id))');
  691. if (DB::isError($res)) {
  692. return $res;
  693. }
  694. // insert yields value 1, nextId call will generate ID 2
  695. return $this->query("INSERT INTO ${seqname} (id) VALUES (0)");
  696. }
  697. // }}}
  698. // {{{ dropSequence()
  699. /**
  700. * Deletes a sequence
  701. *
  702. * @param string $seq_name name of the sequence to be deleted
  703. *
  704. * @return int DB_OK on success. A DB_Error object on failure.
  705. *
  706. * @see DB_common::dropSequence(), DB_common::getSequenceName(),
  707. * DB_mysql::nextID(), DB_mysql::createSequence()
  708. */
  709. function dropSequence($seq_name)
  710. {
  711. return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
  712. }
  713. // }}}
  714. // {{{ _BCsequence()
  715. /**
  716. * Backwards compatibility with old sequence emulation implementation
  717. * (clean up the dupes)
  718. *
  719. * @param string $seqname the sequence name to clean up
  720. *
  721. * @return bool true on success. A DB_Error object on failure.
  722. *
  723. * @access private
  724. */
  725. function _BCsequence($seqname)
  726. {
  727. // Obtain a user-level lock... this will release any previous
  728. // application locks, but unlike LOCK TABLES, it does not abort
  729. // the current transaction and is much less frequently used.
  730. $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
  731. if (DB::isError($result)) {
  732. return $result;
  733. }
  734. if ($result == 0) {
  735. // Failed to get the lock, can't do the conversion, bail
  736. // with a DB_ERROR_NOT_LOCKED error
  737. return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED);
  738. }
  739. $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
  740. if (DB::isError($highest_id)) {
  741. return $highest_id;
  742. }
  743. // This should kill all rows except the highest
  744. // We should probably do something if $highest_id isn't
  745. // numeric, but I'm at a loss as how to handle that...
  746. $result = $this->query('DELETE FROM ' . $seqname
  747. . " WHERE id <> $highest_id");
  748. if (DB::isError($result)) {
  749. return $result;
  750. }
  751. // If another thread has been waiting for this lock,
  752. // it will go thru the above procedure, but will have no
  753. // real effect
  754. $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
  755. if (DB::isError($result)) {
  756. return $result;
  757. }
  758. return true;
  759. }
  760. // }}}
  761. // {{{ quoteIdentifier()
  762. /**
  763. * Quotes a string so it can be safely used as a table or column name
  764. * (WARNING: using names that require this is a REALLY BAD IDEA)
  765. *
  766. * WARNING: Older versions of MySQL can't handle the backtick
  767. * character (<kbd>`</kbd>) in table or column names.
  768. *
  769. * @param string $str identifier name to be quoted
  770. *
  771. * @return string quoted identifier string
  772. *
  773. * @see DB_common::quoteIdentifier()
  774. * @since Method available since Release 1.6.0
  775. */
  776. function quoteIdentifier($str)
  777. {
  778. return '`' . str_replace('`', '``', $str) . '`';
  779. }
  780. // }}}
  781. // {{{ escapeSimple()
  782. /**
  783. * Escapes a string according to the current DBMS's standards
  784. *
  785. * @param string $str the string to be escaped
  786. *
  787. * @return string the escaped string
  788. *
  789. * @see DB_common::quoteSmart()
  790. * @since Method available since Release 1.6.0
  791. */
  792. function escapeSimple($str)
  793. {
  794. return @mysqli_real_escape_string($this->connection, $str);
  795. }
  796. // }}}
  797. // {{{ modifyLimitQuery()
  798. /**
  799. * Adds LIMIT clauses to a query string according to current DBMS standards
  800. *
  801. * @param string $query the query to modify
  802. * @param int $from the row to start to fetching (0 = the first row)
  803. * @param int $count the numbers of rows to fetch
  804. * @param mixed $params array, string or numeric data to be used in
  805. * execution of the statement. Quantity of items
  806. * passed must match quantity of placeholders in
  807. * query: meaning 1 placeholder for non-array
  808. * parameters or 1 placeholder per array element.
  809. *
  810. * @return string the query string with LIMIT clauses added
  811. *
  812. * @access protected
  813. */
  814. function modifyLimitQuery($query, $from, $count, $params = array())
  815. {
  816. if (DB::isManip($query) || $this->_next_query_manip) {
  817. return $query . " LIMIT $count";
  818. } else {
  819. return $query . " LIMIT $from, $count";
  820. }
  821. }
  822. // }}}
  823. // {{{ mysqliRaiseError()
  824. /**
  825. * Produces a DB_Error object regarding the current problem
  826. *
  827. * @param int $errno if the error is being manually raised pass a
  828. * DB_ERROR* constant here. If this isn't passed
  829. * the error information gathered from the DBMS.
  830. *
  831. * @return object the DB_Error object
  832. *
  833. * @see DB_common::raiseError(),
  834. * DB_mysqli::errorNative(), DB_common::errorCode()
  835. */
  836. function mysqliRaiseError($errno = null)
  837. {
  838. if ($errno === null) {
  839. if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
  840. $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT;
  841. $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL;
  842. $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT;
  843. } else {
  844. // Doing this in case mode changes during runtime.
  845. $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS;
  846. $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT;
  847. $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS;
  848. }
  849. $errno = $this->errorCode(mysqli_errno($this->connection));
  850. }
  851. return $this->raiseError($errno, null, null, null,
  852. @mysqli_errno($this->connection) . ' ** ' .
  853. @mysqli_error($this->connection));
  854. }
  855. // }}}
  856. // {{{ errorNative()
  857. /**
  858. * Gets the DBMS' native error code produced by the last query
  859. *
  860. * @return int the DBMS' error code
  861. */
  862. function errorNative()
  863. {
  864. return @mysqli_errno($this->connection);
  865. }
  866. // }}}
  867. // {{{ tableInfo()
  868. /**
  869. * Returns information about a table or a result set
  870. *
  871. * @param object|string $result DB_result object from a query or a
  872. * string containing the name of a table.
  873. * While this also accepts a query result
  874. * resource identifier, this behavior is
  875. * deprecated.
  876. * @param int $mode a valid tableInfo mode
  877. *
  878. * @return array an associative array with the information requested.
  879. * A DB_Error object on failure.
  880. *
  881. * @see DB_common::setOption()
  882. */
  883. function tableInfo($result, $mode = null)
  884. {
  885. if (is_string($result)) {
  886. // Fix for bug #11580.
  887. if ($this->_db) {
  888. if (!@mysqli_select_db($this->connection, $this->_db)) {
  889. return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
  890. }
  891. }
  892. /*
  893. * Probably received a table name.
  894. * Create a result resource identifier.
  895. */
  896. $id = @mysqli_query($this->connection,
  897. "SELECT * FROM $result LIMIT 0");
  898. $got_string = true;
  899. } elseif (isset($result->result)) {
  900. /*
  901. * Probably received a result object.
  902. * Extract the result resource identifier.
  903. */
  904. $id = $result->result;
  905. $got_string = false;
  906. } else {
  907. /*
  908. * Probably received a result resource identifier.
  909. * Copy it.
  910. * Deprecated. Here for compatibility only.
  911. */
  912. $id = $result;
  913. $got_string = false;
  914. }
  915. if (!is_object($id) || !is_a($id, 'mysqli_result')) {
  916. return $this->mysqliRaiseError(DB_ERROR_NEED_MORE_DATA);
  917. }
  918. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
  919. $case_func = 'strtolower';
  920. } else {
  921. $case_func = 'strval';
  922. }
  923. $count = @mysqli_num_fields($id);
  924. $res = array();
  925. if ($mode) {
  926. $res['num_fields'] = $count;
  927. }
  928. for ($i = 0; $i < $count; $i++) {
  929. $tmp = @mysqli_fetch_field($id);
  930. $flags = '';
  931. foreach ($this->mysqli_flags as $const => $means) {
  932. if ($tmp->flags & $const) {
  933. $flags .= $means . ' ';
  934. }
  935. }
  936. if ($tmp->def) {
  937. $flags .= 'default_' . rawurlencode($tmp->def);
  938. }
  939. $flags = trim($flags);
  940. $res[$i] = array(
  941. 'table' => $case_func($tmp->table),
  942. 'name' => $case_func($tmp->name),
  943. 'type' => isset($this->mysqli_types[$tmp->type])
  944. ? $this->mysqli_types[$tmp->type]
  945. : 'unknown',
  946. // http://bugs.php.net/?id=36579
  947. 'len' => $tmp->length,
  948. 'flags' => $flags,
  949. );
  950. if ($mode & DB_TABLEINFO_ORDER) {
  951. $res['order'][$res[$i]['name']] = $i;
  952. }
  953. if ($mode & DB_TABLEINFO_ORDERTABLE) {
  954. $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  955. }
  956. }
  957. // free the result only if we were called on a table
  958. if ($got_string) {
  959. @mysqli_free_result($id);
  960. }
  961. return $res;
  962. }
  963. // }}}
  964. // {{{ getSpecialQuery()
  965. /**
  966. * Obtains the query string needed for listing a given type of objects
  967. *
  968. * @param string $type the kind of objects you want to retrieve
  969. *
  970. * @return string the SQL query string or null if the driver doesn't
  971. * support the object type requested
  972. *
  973. * @access protected
  974. * @see DB_common::getListOf()
  975. */
  976. function getSpecialQuery($type)
  977. {
  978. switch ($type) {
  979. case 'tables':
  980. return 'SHOW TABLES';
  981. case 'users':
  982. return 'SELECT DISTINCT User FROM mysql.user';
  983. case 'databases':
  984. return 'SHOW DATABASES';
  985. default:
  986. return null;
  987. }
  988. }
  989. // }}}
  990. }
  991. /*
  992. * Local variables:
  993. * tab-width: 4
  994. * c-basic-offset: 4
  995. * End:
  996. */
  997. ?>