mysqli.php 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113
  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. public $phptype = 'mysqli';
  54. /**
  55. * The database syntax variant to be used (db2, access, etc.), if any
  56. * @var string
  57. */
  58. public $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. public $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. public $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. public $connection;
  118. /**
  119. * The DSN information for connecting to a database
  120. * @var array
  121. */
  122. public $dsn = array();
  123. /**
  124. * Should data manipulation queries be committed automatically?
  125. * @var bool
  126. * @access private
  127. */
  128. public $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. public $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. public $_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. public $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. public $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. public 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. public 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(
  311. DB_ERROR_CONNECT_FAILED,
  312. null,
  313. null,
  314. null,
  315. $err
  316. );
  317. } else {
  318. return $this->raiseError(
  319. DB_ERROR_CONNECT_FAILED,
  320. null,
  321. null,
  322. null,
  323. $php_errormsg
  324. );
  325. }
  326. }
  327. if ($dsn['database']) {
  328. $this->_db = $dsn['database'];
  329. }
  330. return DB_OK;
  331. }
  332. // }}}
  333. // {{{ disconnect()
  334. /**
  335. * Disconnects from the database server
  336. *
  337. * @return bool TRUE on success, FALSE on failure
  338. */
  339. public function disconnect()
  340. {
  341. $ret = @mysqli_close($this->connection);
  342. $this->connection = null;
  343. return $ret;
  344. }
  345. // }}}
  346. // {{{ simpleQuery()
  347. /**
  348. * Sends a query to the database server
  349. *
  350. * @param string the SQL query string
  351. *
  352. * @return mixed + a PHP result resrouce for successful SELECT queries
  353. * + the DB_OK constant for other successful queries
  354. * + a DB_Error object on failure
  355. */
  356. public function simpleQuery($query)
  357. {
  358. $ismanip = $this->_checkManip($query);
  359. $this->last_query = $query;
  360. $query = $this->modifyQuery($query);
  361. if ($this->_db) {
  362. if (!@mysqli_select_db($this->connection, $this->_db)) {
  363. return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
  364. }
  365. }
  366. if (!$this->autocommit && $ismanip) {
  367. if ($this->transaction_opcount == 0) {
  368. $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=0');
  369. $result = @mysqli_query($this->connection, 'BEGIN');
  370. if (!$result) {
  371. return $this->mysqliRaiseError();
  372. }
  373. }
  374. $this->transaction_opcount++;
  375. }
  376. $result = @mysqli_query($this->connection, $query);
  377. if (!$result) {
  378. return $this->mysqliRaiseError();
  379. }
  380. if (is_object($result)) {
  381. return $result;
  382. }
  383. return DB_OK;
  384. }
  385. // }}}
  386. // {{{ nextResult()
  387. /**
  388. * Move the internal mysql result pointer to the next available result.
  389. *
  390. * This method has not been implemented yet.
  391. *
  392. * @param resource $result a valid sql result resource
  393. * @return false
  394. * @access public
  395. */
  396. public function nextResult($result)
  397. {
  398. return false;
  399. }
  400. // }}}
  401. // {{{ fetchInto()
  402. /**
  403. * Places a row from the result set into the given array
  404. *
  405. * Formating of the array and the data therein are configurable.
  406. * See DB_result::fetchInto() for more information.
  407. *
  408. * This method is not meant to be called directly. Use
  409. * DB_result::fetchInto() instead. It can't be declared "protected"
  410. * because DB_result is a separate object.
  411. *
  412. * @param resource $result the query result resource
  413. * @param array $arr the referenced array to put the data in
  414. * @param int $fetchmode how the resulting array should be indexed
  415. * @param int $rownum the row number to fetch (0 = first row)
  416. *
  417. * @return mixed DB_OK on success, NULL when the end of a result set is
  418. * reached or on failure
  419. *
  420. * @see DB_result::fetchInto()
  421. */
  422. public function fetchInto($result, &$arr, $fetchmode, $rownum = null)
  423. {
  424. if ($rownum !== null) {
  425. if (!@mysqli_data_seek($result, $rownum)) {
  426. return null;
  427. }
  428. }
  429. if ($fetchmode & DB_FETCHMODE_ASSOC) {
  430. $arr = @mysqli_fetch_array($result, MYSQLI_ASSOC);
  431. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
  432. $arr = array_change_key_case($arr, CASE_LOWER);
  433. }
  434. } else {
  435. $arr = @mysqli_fetch_row($result);
  436. }
  437. if (!$arr) {
  438. return null;
  439. }
  440. if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
  441. /*
  442. * Even though this DBMS already trims output, we do this because
  443. * a field might have intentional whitespace at the end that
  444. * gets removed by DB_PORTABILITY_RTRIM under another driver.
  445. */
  446. $this->_rtrimArrayValues($arr);
  447. }
  448. if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
  449. $this->_convertNullArrayValuesToEmpty($arr);
  450. }
  451. return DB_OK;
  452. }
  453. // }}}
  454. // {{{ freeResult()
  455. /**
  456. * Deletes the result set and frees the memory occupied by the result set
  457. *
  458. * This method is not meant to be called directly. Use
  459. * DB_result::free() instead. It can't be declared "protected"
  460. * because DB_result is a separate object.
  461. *
  462. * @param resource $result PHP's query result resource
  463. *
  464. * @return bool TRUE on success, FALSE if $result is invalid
  465. *
  466. * @see DB_result::free()
  467. */
  468. public function freeResult($result)
  469. {
  470. if (! $result instanceof mysqli_result) {
  471. return false;
  472. }
  473. mysqli_free_result($result);
  474. return true;
  475. }
  476. // }}}
  477. // {{{ numCols()
  478. /**
  479. * Gets the number of columns in a result set
  480. *
  481. * This method is not meant to be called directly. Use
  482. * DB_result::numCols() instead. It can't be declared "protected"
  483. * because DB_result is a separate object.
  484. *
  485. * @param resource $result PHP's query result resource
  486. *
  487. * @return int the number of columns. A DB_Error object on failure.
  488. *
  489. * @see DB_result::numCols()
  490. */
  491. public function numCols($result)
  492. {
  493. $cols = @mysqli_num_fields($result);
  494. if (!$cols) {
  495. return $this->mysqliRaiseError();
  496. }
  497. return $cols;
  498. }
  499. // }}}
  500. // {{{ numRows()
  501. /**
  502. * Gets the number of rows in a result set
  503. *
  504. * This method is not meant to be called directly. Use
  505. * DB_result::numRows() instead. It can't be declared "protected"
  506. * because DB_result is a separate object.
  507. *
  508. * @param resource $result PHP's query result resource
  509. *
  510. * @return int the number of rows. A DB_Error object on failure.
  511. *
  512. * @see DB_result::numRows()
  513. */
  514. public function numRows($result)
  515. {
  516. $rows = @mysqli_num_rows($result);
  517. if ($rows === null) {
  518. return $this->mysqliRaiseError();
  519. }
  520. return $rows;
  521. }
  522. // }}}
  523. // {{{ autoCommit()
  524. /**
  525. * Enables or disables automatic commits
  526. *
  527. * @param bool $onoff true turns it on, false turns it off
  528. *
  529. * @return int DB_OK on success. A DB_Error object if the driver
  530. * doesn't support auto-committing transactions.
  531. */
  532. public function autoCommit($onoff = false)
  533. {
  534. // XXX if $this->transaction_opcount > 0, we should probably
  535. // issue a warning here.
  536. $this->autocommit = $onoff ? true : false;
  537. return DB_OK;
  538. }
  539. // }}}
  540. // {{{ commit()
  541. /**
  542. * Commits the current transaction
  543. *
  544. * @return int DB_OK on success. A DB_Error object on failure.
  545. */
  546. public function commit()
  547. {
  548. if ($this->transaction_opcount > 0) {
  549. if ($this->_db) {
  550. if (!@mysqli_select_db($this->connection, $this->_db)) {
  551. return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
  552. }
  553. }
  554. $result = @mysqli_query($this->connection, 'COMMIT');
  555. $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1');
  556. $this->transaction_opcount = 0;
  557. if (!$result) {
  558. return $this->mysqliRaiseError();
  559. }
  560. }
  561. return DB_OK;
  562. }
  563. // }}}
  564. // {{{ rollback()
  565. /**
  566. * Reverts the current transaction
  567. *
  568. * @return int DB_OK on success. A DB_Error object on failure.
  569. */
  570. public function rollback()
  571. {
  572. if ($this->transaction_opcount > 0) {
  573. if ($this->_db) {
  574. if (!@mysqli_select_db($this->connection, $this->_db)) {
  575. return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
  576. }
  577. }
  578. $result = @mysqli_query($this->connection, 'ROLLBACK');
  579. $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1');
  580. $this->transaction_opcount = 0;
  581. if (!$result) {
  582. return $this->mysqliRaiseError();
  583. }
  584. }
  585. return DB_OK;
  586. }
  587. // }}}
  588. // {{{ affectedRows()
  589. /**
  590. * Determines the number of rows affected by a data maniuplation query
  591. *
  592. * 0 is returned for queries that don't manipulate data.
  593. *
  594. * @return int the number of rows. A DB_Error object on failure.
  595. */
  596. public function affectedRows()
  597. {
  598. if ($this->_last_query_manip) {
  599. return @mysqli_affected_rows($this->connection);
  600. } else {
  601. return 0;
  602. }
  603. }
  604. // }}}
  605. // {{{ nextId()
  606. /**
  607. * Returns the next free id in a sequence
  608. *
  609. * @param string $seq_name name of the sequence
  610. * @param boolean $ondemand when true, the seqence is automatically
  611. * created if it does not exist
  612. *
  613. * @return int the next id number in the sequence.
  614. * A DB_Error object on failure.
  615. *
  616. * @see DB_common::nextID(), DB_common::getSequenceName(),
  617. * DB_mysqli::createSequence(), DB_mysqli::dropSequence()
  618. */
  619. public function nextId($seq_name, $ondemand = true)
  620. {
  621. $seqname = $this->getSequenceName($seq_name);
  622. do {
  623. $repeat = 0;
  624. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  625. $result = $this->query('UPDATE ' . $seqname
  626. . ' SET id = LAST_INSERT_ID(id + 1)');
  627. $this->popErrorHandling();
  628. if ($result === DB_OK) {
  629. // COMMON CASE
  630. $id = @mysqli_insert_id($this->connection);
  631. if ($id != 0) {
  632. return $id;
  633. }
  634. // EMPTY SEQ TABLE
  635. // Sequence table must be empty for some reason,
  636. // so fill it and return 1
  637. // Obtain a user-level lock
  638. $result = $this->getOne('SELECT GET_LOCK('
  639. . "'${seqname}_lock', 10)");
  640. if (DB::isError($result)) {
  641. return $this->raiseError($result);
  642. }
  643. if ($result == 0) {
  644. return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED);
  645. }
  646. // add the default value
  647. $result = $this->query('REPLACE INTO ' . $seqname
  648. . ' (id) VALUES (0)');
  649. if (DB::isError($result)) {
  650. return $this->raiseError($result);
  651. }
  652. // Release the lock
  653. $result = $this->getOne('SELECT RELEASE_LOCK('
  654. . "'${seqname}_lock')");
  655. if (DB::isError($result)) {
  656. return $this->raiseError($result);
  657. }
  658. // We know what the result will be, so no need to try again
  659. return 1;
  660. } elseif ($ondemand && DB::isError($result) &&
  661. $result->getCode() == DB_ERROR_NOSUCHTABLE) {
  662. // ONDEMAND TABLE CREATION
  663. $result = $this->createSequence($seq_name);
  664. // Since createSequence initializes the ID to be 1,
  665. // we do not need to retrieve the ID again (or we will get 2)
  666. if (DB::isError($result)) {
  667. return $this->raiseError($result);
  668. } else {
  669. // First ID of a newly created sequence is 1
  670. return 1;
  671. }
  672. } elseif (DB::isError($result) &&
  673. $result->getCode() == DB_ERROR_ALREADY_EXISTS) {
  674. // BACKWARDS COMPAT
  675. // see _BCsequence() comment
  676. $result = $this->_BCsequence($seqname);
  677. if (DB::isError($result)) {
  678. return $this->raiseError($result);
  679. }
  680. $repeat = 1;
  681. }
  682. } while ($repeat);
  683. return $this->raiseError($result);
  684. }
  685. /**
  686. * Creates a new sequence
  687. *
  688. * @param string $seq_name name of the new sequence
  689. *
  690. * @return int DB_OK on success. A DB_Error object on failure.
  691. *
  692. * @see DB_common::createSequence(), DB_common::getSequenceName(),
  693. * DB_mysqli::nextID(), DB_mysqli::dropSequence()
  694. */
  695. public function createSequence($seq_name)
  696. {
  697. $seqname = $this->getSequenceName($seq_name);
  698. $res = $this->query('CREATE TABLE ' . $seqname
  699. . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'
  700. . ' PRIMARY KEY(id))');
  701. if (DB::isError($res)) {
  702. return $res;
  703. }
  704. // insert yields value 1, nextId call will generate ID 2
  705. return $this->query("INSERT INTO ${seqname} (id) VALUES (0)");
  706. }
  707. // }}}
  708. // {{{ dropSequence()
  709. /**
  710. * Deletes a sequence
  711. *
  712. * @param string $seq_name name of the sequence to be deleted
  713. *
  714. * @return int DB_OK on success. A DB_Error object on failure.
  715. *
  716. * @see DB_common::dropSequence(), DB_common::getSequenceName(),
  717. * DB_mysql::nextID(), DB_mysql::createSequence()
  718. */
  719. public function dropSequence($seq_name)
  720. {
  721. return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
  722. }
  723. // }}}
  724. // {{{ _BCsequence()
  725. /**
  726. * Backwards compatibility with old sequence emulation implementation
  727. * (clean up the dupes)
  728. *
  729. * @param string $seqname the sequence name to clean up
  730. *
  731. * @return bool true on success. A DB_Error object on failure.
  732. *
  733. * @access private
  734. */
  735. public function _BCsequence($seqname)
  736. {
  737. // Obtain a user-level lock... this will release any previous
  738. // application locks, but unlike LOCK TABLES, it does not abort
  739. // the current transaction and is much less frequently used.
  740. $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
  741. if (DB::isError($result)) {
  742. return $result;
  743. }
  744. if ($result == 0) {
  745. // Failed to get the lock, can't do the conversion, bail
  746. // with a DB_ERROR_NOT_LOCKED error
  747. return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED);
  748. }
  749. $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
  750. if (DB::isError($highest_id)) {
  751. return $highest_id;
  752. }
  753. // This should kill all rows except the highest
  754. // We should probably do something if $highest_id isn't
  755. // numeric, but I'm at a loss as how to handle that...
  756. $result = $this->query('DELETE FROM ' . $seqname
  757. . " WHERE id <> $highest_id");
  758. if (DB::isError($result)) {
  759. return $result;
  760. }
  761. // If another thread has been waiting for this lock,
  762. // it will go thru the above procedure, but will have no
  763. // real effect
  764. $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
  765. if (DB::isError($result)) {
  766. return $result;
  767. }
  768. return true;
  769. }
  770. // }}}
  771. // {{{ quoteIdentifier()
  772. /**
  773. * Quotes a string so it can be safely used as a table or column name
  774. * (WARNING: using names that require this is a REALLY BAD IDEA)
  775. *
  776. * WARNING: Older versions of MySQL can't handle the backtick
  777. * character (<kbd>`</kbd>) in table or column names.
  778. *
  779. * @param string $str identifier name to be quoted
  780. *
  781. * @return string quoted identifier string
  782. *
  783. * @see DB_common::quoteIdentifier()
  784. * @since Method available since Release 1.6.0
  785. */
  786. public function quoteIdentifier($str)
  787. {
  788. return '`' . str_replace('`', '``', $str) . '`';
  789. }
  790. // }}}
  791. // {{{ escapeSimple()
  792. /**
  793. * Escapes a string according to the current DBMS's standards
  794. *
  795. * @param string $str the string to be escaped
  796. *
  797. * @return string the escaped string
  798. *
  799. * @see DB_common::quoteSmart()
  800. * @since Method available since Release 1.6.0
  801. */
  802. public function escapeSimple($str)
  803. {
  804. return @mysqli_real_escape_string($this->connection, $str);
  805. }
  806. // }}}
  807. // {{{ modifyLimitQuery()
  808. /**
  809. * Adds LIMIT clauses to a query string according to current DBMS standards
  810. *
  811. * @param string $query the query to modify
  812. * @param int $from the row to start to fetching (0 = the first row)
  813. * @param int $count the numbers of rows to fetch
  814. * @param mixed $params array, string or numeric data to be used in
  815. * execution of the statement. Quantity of items
  816. * passed must match quantity of placeholders in
  817. * query: meaning 1 placeholder for non-array
  818. * parameters or 1 placeholder per array element.
  819. *
  820. * @return string the query string with LIMIT clauses added
  821. *
  822. * @access protected
  823. */
  824. public function modifyLimitQuery($query, $from, $count, $params = array())
  825. {
  826. if (DB::isManip($query) || $this->_next_query_manip) {
  827. return $query . " LIMIT $count";
  828. } else {
  829. return $query . " LIMIT $from, $count";
  830. }
  831. }
  832. // }}}
  833. // {{{ mysqliRaiseError()
  834. /**
  835. * Produces a DB_Error object regarding the current problem
  836. *
  837. * @param int $errno if the error is being manually raised pass a
  838. * DB_ERROR* constant here. If this isn't passed
  839. * the error information gathered from the DBMS.
  840. *
  841. * @return object the DB_Error object
  842. *
  843. * @see DB_common::raiseError(),
  844. * DB_mysqli::errorNative(), DB_common::errorCode()
  845. */
  846. public function mysqliRaiseError($errno = null)
  847. {
  848. if ($errno === null) {
  849. if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
  850. $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT;
  851. $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL;
  852. $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT;
  853. } else {
  854. // Doing this in case mode changes during runtime.
  855. $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS;
  856. $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT;
  857. $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS;
  858. }
  859. $errno = $this->errorCode(mysqli_errno($this->connection));
  860. }
  861. return $this->raiseError(
  862. $errno,
  863. null,
  864. null,
  865. null,
  866. @mysqli_errno($this->connection) . ' ** ' .
  867. @mysqli_error($this->connection)
  868. );
  869. }
  870. // }}}
  871. // {{{ errorNative()
  872. /**
  873. * Gets the DBMS' native error code produced by the last query
  874. *
  875. * @return int the DBMS' error code
  876. */
  877. public function errorNative()
  878. {
  879. return @mysqli_errno($this->connection);
  880. }
  881. // }}}
  882. // {{{ tableInfo()
  883. /**
  884. * Returns information about a table or a result set
  885. *
  886. * @param object|string $result DB_result object from a query or a
  887. * string containing the name of a table.
  888. * While this also accepts a query result
  889. * resource identifier, this behavior is
  890. * deprecated.
  891. * @param int $mode a valid tableInfo mode
  892. *
  893. * @return array an associative array with the information requested.
  894. * A DB_Error object on failure.
  895. *
  896. * @see DB_common::setOption()
  897. */
  898. public function tableInfo($result, $mode = null)
  899. {
  900. if (is_string($result)) {
  901. // Fix for bug #11580.
  902. if ($this->_db) {
  903. if (!@mysqli_select_db($this->connection, $this->_db)) {
  904. return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
  905. }
  906. }
  907. /*
  908. * Probably received a table name.
  909. * Create a result resource identifier.
  910. */
  911. $id = @mysqli_query(
  912. $this->connection,
  913. "SELECT * FROM $result LIMIT 0"
  914. );
  915. $got_string = true;
  916. } elseif (isset($result->result)) {
  917. /*
  918. * Probably received a result object.
  919. * Extract the result resource identifier.
  920. */
  921. $id = $result->result;
  922. $got_string = false;
  923. } else {
  924. /*
  925. * Probably received a result resource identifier.
  926. * Copy it.
  927. * Deprecated. Here for compatibility only.
  928. */
  929. $id = $result;
  930. $got_string = false;
  931. }
  932. if (!is_object($id) || !is_a($id, 'mysqli_result')) {
  933. return $this->mysqliRaiseError(DB_ERROR_NEED_MORE_DATA);
  934. }
  935. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
  936. $case_func = 'strtolower';
  937. } else {
  938. $case_func = 'strval';
  939. }
  940. $count = @mysqli_num_fields($id);
  941. $res = array();
  942. if ($mode) {
  943. $res['num_fields'] = $count;
  944. }
  945. for ($i = 0; $i < $count; $i++) {
  946. $tmp = @mysqli_fetch_field($id);
  947. $flags = '';
  948. foreach ($this->mysqli_flags as $const => $means) {
  949. if ($tmp->flags & $const) {
  950. $flags .= $means . ' ';
  951. }
  952. }
  953. if ($tmp->def) {
  954. $flags .= 'default_' . rawurlencode($tmp->def);
  955. }
  956. $flags = trim($flags);
  957. $res[$i] = array(
  958. 'table' => $case_func($tmp->table),
  959. 'name' => $case_func($tmp->name),
  960. 'type' => isset($this->mysqli_types[$tmp->type])
  961. ? $this->mysqli_types[$tmp->type]
  962. : 'unknown',
  963. // http://bugs.php.net/?id=36579
  964. // Doc Bug #36579: mysqli_fetch_field length handling
  965. // https://bugs.php.net/bug.php?id=62426
  966. // Bug #62426: mysqli_fetch_field_direct returns incorrect
  967. // length on UTF8 fields
  968. 'len' => $tmp->length,
  969. 'flags' => $flags,
  970. );
  971. if ($mode & DB_TABLEINFO_ORDER) {
  972. $res['order'][$res[$i]['name']] = $i;
  973. }
  974. if ($mode & DB_TABLEINFO_ORDERTABLE) {
  975. $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  976. }
  977. }
  978. // free the result only if we were called on a table
  979. if ($got_string) {
  980. @mysqli_free_result($id);
  981. }
  982. return $res;
  983. }
  984. // }}}
  985. // {{{ getSpecialQuery()
  986. /**
  987. * Obtains the query string needed for listing a given type of objects
  988. *
  989. * @param string $type the kind of objects you want to retrieve
  990. *
  991. * @return string the SQL query string or null if the driver doesn't
  992. * support the object type requested
  993. *
  994. * @access protected
  995. * @see DB_common::getListOf()
  996. */
  997. public function getSpecialQuery($type)
  998. {
  999. switch ($type) {
  1000. case 'tables':
  1001. return 'SHOW TABLES';
  1002. case 'users':
  1003. return 'SELECT DISTINCT User FROM mysql.user';
  1004. case 'databases':
  1005. return 'SHOW DATABASES';
  1006. default:
  1007. return null;
  1008. }
  1009. }
  1010. public function getVersion() {
  1011. return mysqli_get_server_version($this->connection);
  1012. }
  1013. // }}}
  1014. }
  1015. /*
  1016. * Local variables:
  1017. * tab-width: 4
  1018. * c-basic-offset: 4
  1019. * End:
  1020. */