mysqli.php 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101
  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.9.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>parent::__construct()</kbd>
  209. *
  210. * @return void
  211. */
  212. function __construct()
  213. {
  214. parent::__construct();
  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. if (! $result instanceof mysqli_result) {
  463. return false;
  464. }
  465. mysqli_free_result($result);
  466. return true;
  467. }
  468. // }}}
  469. // {{{ numCols()
  470. /**
  471. * Gets the number of columns in a result set
  472. *
  473. * This method is not meant to be called directly. Use
  474. * DB_result::numCols() instead. It can't be declared "protected"
  475. * because DB_result is a separate object.
  476. *
  477. * @param resource $result PHP's query result resource
  478. *
  479. * @return int the number of columns. A DB_Error object on failure.
  480. *
  481. * @see DB_result::numCols()
  482. */
  483. function numCols($result)
  484. {
  485. $cols = @mysqli_num_fields($result);
  486. if (!$cols) {
  487. return $this->mysqliRaiseError();
  488. }
  489. return $cols;
  490. }
  491. // }}}
  492. // {{{ numRows()
  493. /**
  494. * Gets the number of rows in a result set
  495. *
  496. * This method is not meant to be called directly. Use
  497. * DB_result::numRows() instead. It can't be declared "protected"
  498. * because DB_result is a separate object.
  499. *
  500. * @param resource $result PHP's query result resource
  501. *
  502. * @return int the number of rows. A DB_Error object on failure.
  503. *
  504. * @see DB_result::numRows()
  505. */
  506. function numRows($result)
  507. {
  508. $rows = @mysqli_num_rows($result);
  509. if ($rows === null) {
  510. return $this->mysqliRaiseError();
  511. }
  512. return $rows;
  513. }
  514. // }}}
  515. // {{{ autoCommit()
  516. /**
  517. * Enables or disables automatic commits
  518. *
  519. * @param bool $onoff true turns it on, false turns it off
  520. *
  521. * @return int DB_OK on success. A DB_Error object if the driver
  522. * doesn't support auto-committing transactions.
  523. */
  524. function autoCommit($onoff = false)
  525. {
  526. // XXX if $this->transaction_opcount > 0, we should probably
  527. // issue a warning here.
  528. $this->autocommit = $onoff ? true : false;
  529. return DB_OK;
  530. }
  531. // }}}
  532. // {{{ commit()
  533. /**
  534. * Commits the current transaction
  535. *
  536. * @return int DB_OK on success. A DB_Error object on failure.
  537. */
  538. function commit()
  539. {
  540. if ($this->transaction_opcount > 0) {
  541. if ($this->_db) {
  542. if (!@mysqli_select_db($this->connection, $this->_db)) {
  543. return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
  544. }
  545. }
  546. $result = @mysqli_query($this->connection, 'COMMIT');
  547. $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1');
  548. $this->transaction_opcount = 0;
  549. if (!$result) {
  550. return $this->mysqliRaiseError();
  551. }
  552. }
  553. return DB_OK;
  554. }
  555. // }}}
  556. // {{{ rollback()
  557. /**
  558. * Reverts the current transaction
  559. *
  560. * @return int DB_OK on success. A DB_Error object on failure.
  561. */
  562. function rollback()
  563. {
  564. if ($this->transaction_opcount > 0) {
  565. if ($this->_db) {
  566. if (!@mysqli_select_db($this->connection, $this->_db)) {
  567. return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
  568. }
  569. }
  570. $result = @mysqli_query($this->connection, 'ROLLBACK');
  571. $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1');
  572. $this->transaction_opcount = 0;
  573. if (!$result) {
  574. return $this->mysqliRaiseError();
  575. }
  576. }
  577. return DB_OK;
  578. }
  579. // }}}
  580. // {{{ affectedRows()
  581. /**
  582. * Determines the number of rows affected by a data maniuplation query
  583. *
  584. * 0 is returned for queries that don't manipulate data.
  585. *
  586. * @return int the number of rows. A DB_Error object on failure.
  587. */
  588. function affectedRows()
  589. {
  590. if ($this->_last_query_manip) {
  591. return @mysqli_affected_rows($this->connection);
  592. } else {
  593. return 0;
  594. }
  595. }
  596. // }}}
  597. // {{{ nextId()
  598. /**
  599. * Returns the next free id in a sequence
  600. *
  601. * @param string $seq_name name of the sequence
  602. * @param boolean $ondemand when true, the seqence is automatically
  603. * created if it does not exist
  604. *
  605. * @return int the next id number in the sequence.
  606. * A DB_Error object on failure.
  607. *
  608. * @see DB_common::nextID(), DB_common::getSequenceName(),
  609. * DB_mysqli::createSequence(), DB_mysqli::dropSequence()
  610. */
  611. function nextId($seq_name, $ondemand = true)
  612. {
  613. $seqname = $this->getSequenceName($seq_name);
  614. do {
  615. $repeat = 0;
  616. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  617. $result = $this->query('UPDATE ' . $seqname
  618. . ' SET id = LAST_INSERT_ID(id + 1)');
  619. $this->popErrorHandling();
  620. if ($result === DB_OK) {
  621. // COMMON CASE
  622. $id = @mysqli_insert_id($this->connection);
  623. if ($id != 0) {
  624. return $id;
  625. }
  626. // EMPTY SEQ TABLE
  627. // Sequence table must be empty for some reason,
  628. // so fill it and return 1
  629. // Obtain a user-level lock
  630. $result = $this->getOne('SELECT GET_LOCK('
  631. . "'${seqname}_lock', 10)");
  632. if (DB::isError($result)) {
  633. return $this->raiseError($result);
  634. }
  635. if ($result == 0) {
  636. return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED);
  637. }
  638. // add the default value
  639. $result = $this->query('REPLACE INTO ' . $seqname
  640. . ' (id) VALUES (0)');
  641. if (DB::isError($result)) {
  642. return $this->raiseError($result);
  643. }
  644. // Release the lock
  645. $result = $this->getOne('SELECT RELEASE_LOCK('
  646. . "'${seqname}_lock')");
  647. if (DB::isError($result)) {
  648. return $this->raiseError($result);
  649. }
  650. // We know what the result will be, so no need to try again
  651. return 1;
  652. } elseif ($ondemand && DB::isError($result) &&
  653. $result->getCode() == DB_ERROR_NOSUCHTABLE)
  654. {
  655. // ONDEMAND TABLE CREATION
  656. $result = $this->createSequence($seq_name);
  657. // Since createSequence initializes the ID to be 1,
  658. // we do not need to retrieve the ID again (or we will get 2)
  659. if (DB::isError($result)) {
  660. return $this->raiseError($result);
  661. } else {
  662. // First ID of a newly created sequence is 1
  663. return 1;
  664. }
  665. } elseif (DB::isError($result) &&
  666. $result->getCode() == DB_ERROR_ALREADY_EXISTS)
  667. {
  668. // BACKWARDS COMPAT
  669. // see _BCsequence() comment
  670. $result = $this->_BCsequence($seqname);
  671. if (DB::isError($result)) {
  672. return $this->raiseError($result);
  673. }
  674. $repeat = 1;
  675. }
  676. } while ($repeat);
  677. return $this->raiseError($result);
  678. }
  679. /**
  680. * Creates a new sequence
  681. *
  682. * @param string $seq_name name of the new sequence
  683. *
  684. * @return int DB_OK on success. A DB_Error object on failure.
  685. *
  686. * @see DB_common::createSequence(), DB_common::getSequenceName(),
  687. * DB_mysqli::nextID(), DB_mysqli::dropSequence()
  688. */
  689. function createSequence($seq_name)
  690. {
  691. $seqname = $this->getSequenceName($seq_name);
  692. $res = $this->query('CREATE TABLE ' . $seqname
  693. . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'
  694. . ' PRIMARY KEY(id))');
  695. if (DB::isError($res)) {
  696. return $res;
  697. }
  698. // insert yields value 1, nextId call will generate ID 2
  699. return $this->query("INSERT INTO ${seqname} (id) VALUES (0)");
  700. }
  701. // }}}
  702. // {{{ dropSequence()
  703. /**
  704. * Deletes a sequence
  705. *
  706. * @param string $seq_name name of the sequence to be deleted
  707. *
  708. * @return int DB_OK on success. A DB_Error object on failure.
  709. *
  710. * @see DB_common::dropSequence(), DB_common::getSequenceName(),
  711. * DB_mysql::nextID(), DB_mysql::createSequence()
  712. */
  713. function dropSequence($seq_name)
  714. {
  715. return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
  716. }
  717. // }}}
  718. // {{{ _BCsequence()
  719. /**
  720. * Backwards compatibility with old sequence emulation implementation
  721. * (clean up the dupes)
  722. *
  723. * @param string $seqname the sequence name to clean up
  724. *
  725. * @return bool true on success. A DB_Error object on failure.
  726. *
  727. * @access private
  728. */
  729. function _BCsequence($seqname)
  730. {
  731. // Obtain a user-level lock... this will release any previous
  732. // application locks, but unlike LOCK TABLES, it does not abort
  733. // the current transaction and is much less frequently used.
  734. $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
  735. if (DB::isError($result)) {
  736. return $result;
  737. }
  738. if ($result == 0) {
  739. // Failed to get the lock, can't do the conversion, bail
  740. // with a DB_ERROR_NOT_LOCKED error
  741. return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED);
  742. }
  743. $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
  744. if (DB::isError($highest_id)) {
  745. return $highest_id;
  746. }
  747. // This should kill all rows except the highest
  748. // We should probably do something if $highest_id isn't
  749. // numeric, but I'm at a loss as how to handle that...
  750. $result = $this->query('DELETE FROM ' . $seqname
  751. . " WHERE id <> $highest_id");
  752. if (DB::isError($result)) {
  753. return $result;
  754. }
  755. // If another thread has been waiting for this lock,
  756. // it will go thru the above procedure, but will have no
  757. // real effect
  758. $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
  759. if (DB::isError($result)) {
  760. return $result;
  761. }
  762. return true;
  763. }
  764. // }}}
  765. // {{{ quoteIdentifier()
  766. /**
  767. * Quotes a string so it can be safely used as a table or column name
  768. * (WARNING: using names that require this is a REALLY BAD IDEA)
  769. *
  770. * WARNING: Older versions of MySQL can't handle the backtick
  771. * character (<kbd>`</kbd>) in table or column names.
  772. *
  773. * @param string $str identifier name to be quoted
  774. *
  775. * @return string quoted identifier string
  776. *
  777. * @see DB_common::quoteIdentifier()
  778. * @since Method available since Release 1.6.0
  779. */
  780. function quoteIdentifier($str)
  781. {
  782. return '`' . str_replace('`', '``', $str) . '`';
  783. }
  784. // }}}
  785. // {{{ escapeSimple()
  786. /**
  787. * Escapes a string according to the current DBMS's standards
  788. *
  789. * @param string $str the string to be escaped
  790. *
  791. * @return string the escaped string
  792. *
  793. * @see DB_common::quoteSmart()
  794. * @since Method available since Release 1.6.0
  795. */
  796. function escapeSimple($str)
  797. {
  798. return @mysqli_real_escape_string($this->connection, $str);
  799. }
  800. // }}}
  801. // {{{ modifyLimitQuery()
  802. /**
  803. * Adds LIMIT clauses to a query string according to current DBMS standards
  804. *
  805. * @param string $query the query to modify
  806. * @param int $from the row to start to fetching (0 = the first row)
  807. * @param int $count the numbers of rows to fetch
  808. * @param mixed $params array, string or numeric data to be used in
  809. * execution of the statement. Quantity of items
  810. * passed must match quantity of placeholders in
  811. * query: meaning 1 placeholder for non-array
  812. * parameters or 1 placeholder per array element.
  813. *
  814. * @return string the query string with LIMIT clauses added
  815. *
  816. * @access protected
  817. */
  818. function modifyLimitQuery($query, $from, $count, $params = array())
  819. {
  820. if (DB::isManip($query) || $this->_next_query_manip) {
  821. return $query . " LIMIT $count";
  822. } else {
  823. return $query . " LIMIT $from, $count";
  824. }
  825. }
  826. // }}}
  827. // {{{ mysqliRaiseError()
  828. /**
  829. * Produces a DB_Error object regarding the current problem
  830. *
  831. * @param int $errno if the error is being manually raised pass a
  832. * DB_ERROR* constant here. If this isn't passed
  833. * the error information gathered from the DBMS.
  834. *
  835. * @return object the DB_Error object
  836. *
  837. * @see DB_common::raiseError(),
  838. * DB_mysqli::errorNative(), DB_common::errorCode()
  839. */
  840. function mysqliRaiseError($errno = null)
  841. {
  842. if ($errno === null) {
  843. if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
  844. $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT;
  845. $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL;
  846. $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT;
  847. } else {
  848. // Doing this in case mode changes during runtime.
  849. $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS;
  850. $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT;
  851. $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS;
  852. }
  853. $errno = $this->errorCode(mysqli_errno($this->connection));
  854. }
  855. return $this->raiseError($errno, null, null, null,
  856. @mysqli_errno($this->connection) . ' ** ' .
  857. @mysqli_error($this->connection));
  858. }
  859. // }}}
  860. // {{{ errorNative()
  861. /**
  862. * Gets the DBMS' native error code produced by the last query
  863. *
  864. * @return int the DBMS' error code
  865. */
  866. function errorNative()
  867. {
  868. return @mysqli_errno($this->connection);
  869. }
  870. // }}}
  871. // {{{ tableInfo()
  872. /**
  873. * Returns information about a table or a result set
  874. *
  875. * @param object|string $result DB_result object from a query or a
  876. * string containing the name of a table.
  877. * While this also accepts a query result
  878. * resource identifier, this behavior is
  879. * deprecated.
  880. * @param int $mode a valid tableInfo mode
  881. *
  882. * @return array an associative array with the information requested.
  883. * A DB_Error object on failure.
  884. *
  885. * @see DB_common::setOption()
  886. */
  887. function tableInfo($result, $mode = null)
  888. {
  889. if (is_string($result)) {
  890. // Fix for bug #11580.
  891. if ($this->_db) {
  892. if (!@mysqli_select_db($this->connection, $this->_db)) {
  893. return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
  894. }
  895. }
  896. /*
  897. * Probably received a table name.
  898. * Create a result resource identifier.
  899. */
  900. $id = @mysqli_query($this->connection,
  901. "SELECT * FROM $result LIMIT 0");
  902. $got_string = true;
  903. } elseif (isset($result->result)) {
  904. /*
  905. * Probably received a result object.
  906. * Extract the result resource identifier.
  907. */
  908. $id = $result->result;
  909. $got_string = false;
  910. } else {
  911. /*
  912. * Probably received a result resource identifier.
  913. * Copy it.
  914. * Deprecated. Here for compatibility only.
  915. */
  916. $id = $result;
  917. $got_string = false;
  918. }
  919. if (!is_object($id) || !is_a($id, 'mysqli_result')) {
  920. return $this->mysqliRaiseError(DB_ERROR_NEED_MORE_DATA);
  921. }
  922. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
  923. $case_func = 'strtolower';
  924. } else {
  925. $case_func = 'strval';
  926. }
  927. $count = @mysqli_num_fields($id);
  928. $res = array();
  929. if ($mode) {
  930. $res['num_fields'] = $count;
  931. }
  932. for ($i = 0; $i < $count; $i++) {
  933. $tmp = @mysqli_fetch_field($id);
  934. $flags = '';
  935. foreach ($this->mysqli_flags as $const => $means) {
  936. if ($tmp->flags & $const) {
  937. $flags .= $means . ' ';
  938. }
  939. }
  940. if ($tmp->def) {
  941. $flags .= 'default_' . rawurlencode($tmp->def);
  942. }
  943. $flags = trim($flags);
  944. $res[$i] = array(
  945. 'table' => $case_func($tmp->table),
  946. 'name' => $case_func($tmp->name),
  947. 'type' => isset($this->mysqli_types[$tmp->type])
  948. ? $this->mysqli_types[$tmp->type]
  949. : 'unknown',
  950. // http://bugs.php.net/?id=36579
  951. // Doc Bug #36579: mysqli_fetch_field length handling
  952. // https://bugs.php.net/bug.php?id=62426
  953. // Bug #62426: mysqli_fetch_field_direct returns incorrect
  954. // length on UTF8 fields
  955. 'len' => $tmp->length,
  956. 'flags' => $flags,
  957. );
  958. if ($mode & DB_TABLEINFO_ORDER) {
  959. $res['order'][$res[$i]['name']] = $i;
  960. }
  961. if ($mode & DB_TABLEINFO_ORDERTABLE) {
  962. $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  963. }
  964. }
  965. // free the result only if we were called on a table
  966. if ($got_string) {
  967. @mysqli_free_result($id);
  968. }
  969. return $res;
  970. }
  971. // }}}
  972. // {{{ getSpecialQuery()
  973. /**
  974. * Obtains the query string needed for listing a given type of objects
  975. *
  976. * @param string $type the kind of objects you want to retrieve
  977. *
  978. * @return string the SQL query string or null if the driver doesn't
  979. * support the object type requested
  980. *
  981. * @access protected
  982. * @see DB_common::getListOf()
  983. */
  984. function getSpecialQuery($type)
  985. {
  986. switch ($type) {
  987. case 'tables':
  988. return 'SHOW TABLES';
  989. case 'users':
  990. return 'SELECT DISTINCT User FROM mysql.user';
  991. case 'databases':
  992. return 'SHOW DATABASES';
  993. default:
  994. return null;
  995. }
  996. }
  997. // }}}
  998. }
  999. /*
  1000. * Local variables:
  1001. * tab-width: 4
  1002. * c-basic-offset: 4
  1003. * End:
  1004. */
  1005. ?>