mysql.php 32 KB

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