fbsql.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915
  1. <?php
  2. // vim: set et ts=4 sw=4 fdm=marker:
  3. // +----------------------------------------------------------------------+
  4. // | PHP versions 4 and 5 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
  7. // | Stig. S. Bakken, Lukas Smith, Frank M. Kromann |
  8. // | All rights reserved. |
  9. // +----------------------------------------------------------------------+
  10. // | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
  11. // | API as well as database abstraction for PHP applications. |
  12. // | This LICENSE is in the BSD license style. |
  13. // | |
  14. // | Redistribution and use in source and binary forms, with or without |
  15. // | modification, are permitted provided that the following conditions |
  16. // | are met: |
  17. // | |
  18. // | Redistributions of source code must retain the above copyright |
  19. // | notice, this list of conditions and the following disclaimer. |
  20. // | |
  21. // | Redistributions in binary form must reproduce the above copyright |
  22. // | notice, this list of conditions and the following disclaimer in the |
  23. // | documentation and/or other materials provided with the distribution. |
  24. // | |
  25. // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
  26. // | Lukas Smith nor the names of his contributors may be used to endorse |
  27. // | or promote products derived from this software without specific prior|
  28. // | written permission. |
  29. // | |
  30. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
  31. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
  32. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
  33. // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
  34. // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
  35. // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
  36. // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
  37. // | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
  38. // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
  39. // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
  40. // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
  41. // | POSSIBILITY OF SUCH DAMAGE. |
  42. // +----------------------------------------------------------------------+
  43. // | Author: Lukas Smith <smith@pooteeweet.org> |
  44. // +----------------------------------------------------------------------+
  45. //
  46. // $Id$
  47. //
  48. /**
  49. * MDB2 FrontBase driver
  50. *
  51. * @package MDB2
  52. * @category Database
  53. * @author Lukas Smith <smith@pooteeweet.org>
  54. * @author Frank M. Kromann <frank@kromann.info>
  55. */
  56. class MDB2_Driver_fbsql extends MDB2_Driver_Common
  57. {
  58. // {{{ properties
  59. var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => "'", 'escape_pattern' => false);
  60. var $identifier_quoting = array('start' => '"', 'end' => '"', 'escape' => '"');
  61. // }}}
  62. // {{{ constructor
  63. /**
  64. * Constructor
  65. */
  66. function __construct()
  67. {
  68. parent::__construct();
  69. $this->phptype = 'fbsql';
  70. $this->dbsyntax = 'fbsql';
  71. $this->supported['sequences'] = 'emulated';
  72. $this->supported['indexes'] = true;
  73. $this->supported['affected_rows'] = true;
  74. $this->supported['transactions'] = true;
  75. $this->supported['savepoints'] = false;
  76. $this->supported['summary_functions'] = true;
  77. $this->supported['order_by_text'] = true;
  78. $this->supported['current_id'] = 'emulated';
  79. $this->supported['limit_queries'] = 'emulated';
  80. $this->supported['LOBs'] = true;
  81. $this->supported['replace'] ='emulated';
  82. $this->supported['sub_selects'] = true;
  83. $this->supported['auto_increment'] = false; // not implemented
  84. $this->supported['primary_key'] = true;
  85. $this->supported['result_introspection'] = true;
  86. $this->supported['prepared_statements'] = 'emulated';
  87. $this->supported['identifier_quoting'] = true;
  88. $this->supported['pattern_escaping'] = false;
  89. $this->supported['new_link'] = false;
  90. $this->options['DBA_username'] = false;
  91. $this->options['DBA_password'] = false;
  92. }
  93. // }}}
  94. // {{{ errorInfo()
  95. /**
  96. * This method is used to collect information about an error
  97. *
  98. * @param integer $error
  99. * @return array
  100. * @access public
  101. */
  102. function errorInfo($error = null)
  103. {
  104. if ($this->connection) {
  105. $native_code = @fbsql_errno($this->connection);
  106. $native_msg = @fbsql_error($this->connection);
  107. } else {
  108. $native_code = @fbsql_errno();
  109. $native_msg = @fbsql_error();
  110. }
  111. if (null === $error) {
  112. static $ecode_map;
  113. if (empty($ecode_map)) {
  114. $ecode_map = array(
  115. 22 => MDB2_ERROR_SYNTAX,
  116. 85 => MDB2_ERROR_ALREADY_EXISTS,
  117. 108 => MDB2_ERROR_SYNTAX,
  118. 116 => MDB2_ERROR_NOSUCHTABLE,
  119. 124 => MDB2_ERROR_VALUE_COUNT_ON_ROW,
  120. 215 => MDB2_ERROR_NOSUCHFIELD,
  121. 217 => MDB2_ERROR_INVALID_NUMBER,
  122. 226 => MDB2_ERROR_NOSUCHFIELD,
  123. 231 => MDB2_ERROR_INVALID,
  124. 239 => MDB2_ERROR_TRUNCATED,
  125. 251 => MDB2_ERROR_SYNTAX,
  126. 266 => MDB2_ERROR_NOT_FOUND,
  127. 357 => MDB2_ERROR_CONSTRAINT_NOT_NULL,
  128. 358 => MDB2_ERROR_CONSTRAINT,
  129. 360 => MDB2_ERROR_CONSTRAINT,
  130. 361 => MDB2_ERROR_CONSTRAINT,
  131. );
  132. }
  133. if (isset($ecode_map[$native_code])) {
  134. $error = $ecode_map[$native_code];
  135. }
  136. }
  137. return array($error, $native_code, $native_msg);
  138. }
  139. // }}}
  140. // {{{ beginTransaction()
  141. /**
  142. * Start a transaction or set a savepoint.
  143. *
  144. * @param string name of a savepoint to set
  145. * @return mixed MDB2_OK on success, a MDB2 error on failure
  146. *
  147. * @access public
  148. */
  149. function beginTransaction($savepoint = null)
  150. {
  151. $this->debug('Starting transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
  152. if (null !== $savepoint) {
  153. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  154. 'savepoints are not supported', __FUNCTION__);
  155. }
  156. if ($this->in_transaction) {
  157. return MDB2_OK; //nothing to do
  158. }
  159. if (!$this->destructor_registered && $this->opened_persistent) {
  160. $this->destructor_registered = true;
  161. register_shutdown_function('MDB2_closeOpenTransactions');
  162. }
  163. $result =& $this->_doQuery('SET COMMIT FALSE;', true);
  164. if (MDB2::isError($result)) {
  165. return $result;
  166. }
  167. $this->in_transaction = true;
  168. return MDB2_OK;
  169. }
  170. // }}}
  171. // {{{ commit()
  172. /**
  173. * Commit the database changes done during a transaction that is in
  174. * progress or release a savepoint. This function may only be called when
  175. * auto-committing is disabled, otherwise it will fail. Therefore, a new
  176. * transaction is implicitly started after committing the pending changes.
  177. *
  178. * @param string name of a savepoint to release
  179. * @return mixed MDB2_OK on success, a MDB2 error on failure
  180. *
  181. * @access public
  182. */
  183. function commit($savepoint = null)
  184. {
  185. $this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
  186. if (!$this->in_transaction) {
  187. return $this->raiseError(MDB2_ERROR_INVALID, null, null,
  188. 'commit/release savepoint cannot be done changes are auto committed', __FUNCTION__);
  189. }
  190. if (null !== $savepoint) {
  191. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  192. 'savepoints are not supported', __FUNCTION__);
  193. }
  194. $result =& $this->_doQuery('COMMIT;', true);
  195. if (MDB2::isError($result)) {
  196. return $result;
  197. }
  198. $result =& $this->_doQuery('SET COMMIT TRUE;', true);
  199. if (MDB2::isError($result)) {
  200. return $result;
  201. }
  202. $this->in_transaction = false;
  203. return MDB2_OK;
  204. }
  205. // }}}
  206. // {{{
  207. /**
  208. * Cancel any database changes done during a transaction or since a specific
  209. * savepoint that is in progress. This function may only be called when
  210. * auto-committing is disabled, otherwise it will fail. Therefore, a new
  211. * transaction is implicitly started after canceling the pending changes.
  212. *
  213. * @param string name of a savepoint to rollback to
  214. * @return mixed MDB2_OK on success, a MDB2 error on failure
  215. *
  216. * @access public
  217. */
  218. function rollback($savepoint = null)
  219. {
  220. $this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
  221. if (!$this->in_transaction) {
  222. return $this->raiseError(MDB2_ERROR_INVALID, null, null,
  223. 'rollback cannot be done changes are auto committed', __FUNCTION__);
  224. }
  225. if (null !== $savepoint) {
  226. return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  227. 'savepoints are not supported', __FUNCTION__);
  228. }
  229. $result =& $this->_doQuery('ROLLBACK;', true);
  230. if (MDB2::isError($result)) {
  231. return $result;
  232. }
  233. $result =& $this->_doQuery('SET COMMIT TRUE;', true);
  234. if (MDB2::isError($result)) {
  235. return $result;
  236. }
  237. $this->in_transaction = false;
  238. return MDB2_OK;
  239. }
  240. // }}}
  241. // {{{ _doConnect()
  242. /**
  243. * do the grunt work of the connect
  244. *
  245. * @return connection on success or MDB2 Error Object on failure
  246. * @access protected
  247. */
  248. function _doConnect($username, $password, $persistent = false)
  249. {
  250. if (!extension_loaded($this->phptype)) {
  251. return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  252. 'extension '.$this->phptype.' is not compiled into PHP', __FUNCTION__);
  253. }
  254. $params = array(
  255. $this->dsn['hostspec'] ? $this->dsn['hostspec'] : 'localhost',
  256. $username ? $username : null,
  257. $password ? $password : null,
  258. );
  259. $connect_function = $persistent ? 'fbsql_pconnect' : 'fbsql_connect';
  260. $connection = @call_user_func_array($connect_function, $params);
  261. if ($connection <= 0) {
  262. return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
  263. 'unable to establish a connection', __FUNCTION__);
  264. }
  265. if (!empty($this->dsn['charset'])) {
  266. $result = $this->setCharset($this->dsn['charset'], $connection);
  267. if (MDB2::isError($result)) {
  268. return $result;
  269. }
  270. }
  271. return $connection;
  272. }
  273. // }}}
  274. // {{{ connect()
  275. /**
  276. * Connect to the database
  277. *
  278. * @return true on success, MDB2 Error Object on failure
  279. **/
  280. function connect()
  281. {
  282. if (is_resource($this->connection)) {
  283. //if (count(array_diff($this->connected_dsn, $this->dsn)) == 0
  284. if (MDB2::areEquals($this->connected_dsn, $this->dsn)
  285. && $this->opened_persistent == $this->options['persistent']
  286. ) {
  287. return MDB2_OK;
  288. }
  289. $this->disconnect(false);
  290. }
  291. $connection = $this->_doConnect($this->dsn['username'],
  292. $this->dsn['password'],
  293. $this->options['persistent']);
  294. if (MDB2::isError($connection)) {
  295. return $connection;
  296. }
  297. $this->connection = $connection;
  298. $this->connected_dsn = $this->dsn;
  299. $this->connected_database_name = '';
  300. $this->opened_persistent = $this->options['persistent'];
  301. $this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype;
  302. if ($this->database_name) {
  303. if ($this->database_name != $this->connected_database_name) {
  304. if (!@fbsql_select_db($this->database_name, $connection)) {
  305. $err = $this->raiseError(null, null, null,
  306. 'Could not select the database: '.$this->database_name, __FUNCTION__);
  307. return $err;
  308. }
  309. $this->connected_database_name = $this->database_name;
  310. }
  311. }
  312. return MDB2_OK;
  313. }
  314. // }}}
  315. // {{{ databaseExists()
  316. /**
  317. * check if given database name is exists?
  318. *
  319. * @param string $name name of the database that should be checked
  320. *
  321. * @return mixed true/false on success, a MDB2 error on failure
  322. * @access public
  323. */
  324. function databaseExists($name)
  325. {
  326. $connection = $this->_doConnect($this->dsn['username'],
  327. $this->dsn['password'],
  328. $this->options['persistent']);
  329. if (MDB2::isError($connection)) {
  330. return $connection;
  331. }
  332. $result = @fbsql_select_db($name, $connection);
  333. @fbsql_close($connection);
  334. return $result;
  335. }
  336. // }}}
  337. // {{{ disconnect()
  338. /**
  339. * Log out and disconnect from the database.
  340. *
  341. * @param boolean $force if the disconnect should be forced even if the
  342. * connection is opened persistently
  343. * @return mixed true on success, false if not connected and error
  344. * object on error
  345. * @access public
  346. */
  347. function disconnect($force = true)
  348. {
  349. if (is_resource($this->connection)) {
  350. if ($this->in_transaction) {
  351. $dsn = $this->dsn;
  352. $database_name = $this->database_name;
  353. $persistent = $this->options['persistent'];
  354. $this->dsn = $this->connected_dsn;
  355. $this->database_name = $this->connected_database_name;
  356. $this->options['persistent'] = $this->opened_persistent;
  357. $this->rollback();
  358. $this->dsn = $dsn;
  359. $this->database_name = $database_name;
  360. $this->options['persistent'] = $persistent;
  361. }
  362. if (!$this->opened_persistent || $force) {
  363. @fbsql_close($this->connection);
  364. }
  365. }
  366. return parent::disconnect($force);
  367. }
  368. // }}}
  369. // {{{ standaloneQuery()
  370. /**
  371. * execute a query as DBA
  372. *
  373. * @param string $query the SQL query
  374. * @param mixed $types array that contains the types of the columns in
  375. * the result set
  376. * @param boolean $is_manip if the query is a manipulation query
  377. * @return mixed MDB2_OK on success, a MDB2 error on failure
  378. * @access public
  379. */
  380. function &standaloneQuery($query, $types = null, $is_manip = false)
  381. {
  382. $user = $this->options['DBA_username']? $this->options['DBA_username'] : $this->dsn['username'];
  383. $pass = $this->options['DBA_password']? $this->options['DBA_password'] : $this->dsn['password'];
  384. $connection = $this->_doConnect($user, $pass, $this->options['persistent']);
  385. if (MDB2::isError($connection)) {
  386. return $connection;
  387. }
  388. $offset = $this->offset;
  389. $limit = $this->limit;
  390. $this->offset = $this->limit = 0;
  391. $query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
  392. $result =& $this->_doQuery($query, $is_manip, $connection, $this->database_name);
  393. if (!MDB2::isError($result)) {
  394. $result = $this->_affectedRows($connection, $result);
  395. }
  396. @fbsql_close($connection);
  397. return $result;
  398. }
  399. // }}}
  400. // {{{ _doQuery()
  401. /**
  402. * Execute a query
  403. * @param string $query query
  404. * @param boolean $is_manip if the query is a manipulation query
  405. * @param resource $connection
  406. * @param string $database_name
  407. * @return result or error object
  408. * @access protected
  409. */
  410. function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null)
  411. {
  412. $this->last_query = $query;
  413. $result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre'));
  414. if ($result) {
  415. if (MDB2::isError($result)) {
  416. return $result;
  417. }
  418. $query = $result;
  419. }
  420. if ($this->options['disable_query']) {
  421. $result = $is_manip ? 0 : null;
  422. return $result;
  423. }
  424. if (null === $connection) {
  425. $connection = $this->getConnection();
  426. if (MDB2::isError($connection)) {
  427. return $connection;
  428. }
  429. }
  430. if (null === $database_name) {
  431. $database_name = $this->database_name;
  432. }
  433. if ($database_name) {
  434. if ($database_name != $this->connected_database_name) {
  435. if (!@fbsql_select_db($database_name, $connection)) {
  436. $err = $this->raiseError(null, null, null,
  437. 'Could not select the database: '.$database_name, __FUNCTION__);
  438. return $err;
  439. }
  440. $this->connected_database_name = $database_name;
  441. }
  442. }
  443. $result = @fbsql_query($query, $connection);
  444. if (!$result) {
  445. $err =& $this->raiseError(null, null, null,
  446. 'Could not execute statement', __FUNCTION__);
  447. return $err;
  448. }
  449. $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'post', 'result' => $result));
  450. return $result;
  451. }
  452. // }}}
  453. // {{{ _affectedRows()
  454. /**
  455. * Returns the number of rows affected
  456. *
  457. * @param resource $result
  458. * @param resource $connection
  459. * @return mixed MDB2 Error Object or the number of rows affected
  460. * @access private
  461. */
  462. function _affectedRows($connection, $result = null)
  463. {
  464. if (null === $connection) {
  465. $connection = $this->getConnection();
  466. if (MDB2::isError($connection)) {
  467. return $connection;
  468. }
  469. }
  470. return @fbsql_affected_rows($connection);
  471. }
  472. // }}}
  473. // {{{ _modifyQuery()
  474. /**
  475. * Changes a query string for various DBMS specific reasons
  476. *
  477. * @param string $query query to modify
  478. * @param boolean $is_manip if it is a DML query
  479. * @param integer $limit limit the number of rows
  480. * @param integer $offset start reading from given offset
  481. * @return string modified query
  482. * @access protected
  483. */
  484. function _modifyQuery($query, $is_manip, $limit, $offset)
  485. {
  486. if ($limit > 0) {
  487. if ($is_manip) {
  488. return preg_replace('/^([\s(])*SELECT(?!\s*TOP\s*\()/i',
  489. "\\1SELECT TOP($limit)", $query);
  490. } else {
  491. return preg_replace('/([\s(])*SELECT(?!\s*TOP\s*\()/i',
  492. "\\1SELECT TOP($offset,$limit)", $query);
  493. }
  494. }
  495. // Add ; to the end of the query. This is required by FrontBase
  496. return $query.';';
  497. }
  498. // }}}
  499. // {{{ nextID()
  500. /**
  501. * Returns the next free id of a sequence
  502. *
  503. * @param string $seq_name name of the sequence
  504. * @param boolean $ondemand when true the sequence is
  505. * automatic created, if it
  506. * not exists
  507. *
  508. * @return mixed MDB2 Error Object or id
  509. * @access public
  510. */
  511. function nextID($seq_name, $ondemand = true)
  512. {
  513. $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
  514. $seqcol_name = $this->quoteIdentifier($this->options['seqcol_name'], true);
  515. $query = "INSERT INTO $sequence_name ($seqcol_name) VALUES (NULL);";
  516. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  517. $this->expectError(MDB2_ERROR_NOSUCHTABLE);
  518. $result =& $this->_doQuery($query, true);
  519. $this->popExpect();
  520. $this->popErrorHandling();
  521. if (MDB2::isError($result)) {
  522. if ($ondemand && $result->getCode() == MDB2_ERROR_NOSUCHTABLE) {
  523. $this->loadModule('Manager', null, true);
  524. $result = $this->manager->createSequence($seq_name);
  525. if (MDB2::isError($result)) {
  526. return $this->raiseError($result, null, null,
  527. 'on demand sequence '.$seq_name.' could not be created', __FUNCTION__);
  528. } else {
  529. return $this->nextID($seq_name, false);
  530. }
  531. }
  532. return $result;
  533. }
  534. $value = $this->lastInsertID();
  535. if (is_numeric($value)) {
  536. $query = "DELETE FROM $sequence_name WHERE $seqcol_name < $value";
  537. $result =& $this->_doQuery($query, true);
  538. if (MDB2::isError($result)) {
  539. $this->warnings[] = 'nextID: could not delete previous sequence table values from '.$seq_name;
  540. }
  541. }
  542. return $value;
  543. }
  544. // }}}
  545. // {{{ lastInsertID()
  546. /**
  547. * Returns the autoincrement ID if supported or $id or fetches the current
  548. * ID in a sequence called: $table.(empty($field) ? '' : '_'.$field)
  549. *
  550. * @param string $table name of the table into which a new row was inserted
  551. * @param string $field name of the field into which a new row was inserted
  552. * @return mixed MDB2 Error Object or id
  553. * @access public
  554. */
  555. function lastInsertID($table = null, $field = null)
  556. {
  557. $connection = $this->getConnection();
  558. if (MDB2::isError($connection)) {
  559. return $connection;
  560. }
  561. $value = @fbsql_insert_id($connection);
  562. if (!$value) {
  563. return $this->raiseError(null, null, null,
  564. 'Could not get last insert ID', __FUNCTION__);
  565. }
  566. return $value;
  567. }
  568. // }}}
  569. // {{{ currID()
  570. /**
  571. * Returns the current id of a sequence
  572. *
  573. * @param string $seq_name name of the sequence
  574. * @return mixed MDB2 Error Object or id
  575. * @access public
  576. */
  577. function currID($seq_name)
  578. {
  579. $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
  580. $seqcol_name = $this->quoteIdentifier($this->options['seqcol_name'], true);
  581. $query = "SELECT MAX($seqcol_name) FROM $sequence_name";
  582. return $this->queryOne($query, 'integer');
  583. }
  584. }
  585. /**
  586. * MDB2 FrontbaseSQL result driver
  587. *
  588. * @package MDB2
  589. * @category Database
  590. * @author Lukas Smith <smith@pooteeweet.org>
  591. */
  592. class MDB2_Result_fbsql extends MDB2_Result_Common
  593. {
  594. // }}}
  595. // {{{ fetchRow()
  596. /**
  597. * Fetch a row and insert the data into an existing array.
  598. *
  599. * @param int $fetchmode how the array data should be indexed
  600. * @param int $rownum number of the row where the data can be found
  601. * @return int data array on success, a MDB2 error on failure
  602. * @access public
  603. */
  604. function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
  605. {
  606. if (null !== $rownum) {
  607. $seek = $this->seek($rownum);
  608. if (MDB2::isError($seek)) {
  609. return $seek;
  610. }
  611. }
  612. if ($fetchmode == MDB2_FETCHMODE_DEFAULT) {
  613. $fetchmode = $this->db->fetchmode;
  614. }
  615. if ( $fetchmode == MDB2_FETCHMODE_ASSOC
  616. || $fetchmode == MDB2_FETCHMODE_OBJECT
  617. ) {
  618. $row = @fbsql_fetch_assoc($this->result);
  619. if (is_array($row)
  620. && $this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE
  621. ) {
  622. $row = array_change_key_case($row, $this->db->options['field_case']);
  623. }
  624. } else {
  625. $row = @fbsql_fetch_row($this->result);
  626. }
  627. if (!$row) {
  628. if (false === $this->result) {
  629. $err =& $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
  630. 'resultset has already been freed', __FUNCTION__);
  631. return $err;
  632. }
  633. $null = null;
  634. return $null;
  635. }
  636. $mode = $this->db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL;
  637. if ($mode) {
  638. $this->db->_fixResultArrayValues($row, $mode);
  639. }
  640. if (($fetchmode != MDB2_FETCHMODE_ASSOC) && !empty($this->types)) {
  641. $row = $this->db->datatype->convertResultRow($this->types, $row, $rtrim);
  642. } elseif (($fetchmode == MDB2_FETCHMODE_ASSOC) && !empty($this->types_assoc)) {
  643. $row = $this->db->datatype->convertResultRow($this->types_assoc, $row, $rtrim);
  644. }
  645. if (!empty($this->values)) {
  646. $this->_assignBindColumns($row);
  647. }
  648. if ($fetchmode === MDB2_FETCHMODE_OBJECT) {
  649. $object_class = $this->db->options['fetch_class'];
  650. if ($object_class == 'stdClass') {
  651. $row = (object) $row;
  652. } else {
  653. $row = new $object_class($row);
  654. }
  655. }
  656. ++$this->rownum;
  657. return $row;
  658. }
  659. // }}}
  660. // {{{ _getColumnNames()
  661. /**
  662. * Retrieve the names of columns returned by the DBMS in a query result.
  663. *
  664. * @return mixed Array variable that holds the names of columns as keys
  665. * or an MDB2 error on failure.
  666. * Some DBMS may not return any columns when the result set
  667. * does not contain any rows.
  668. * @access private
  669. */
  670. function _getColumnNames()
  671. {
  672. $columns = array();
  673. $numcols = $this->numCols();
  674. if (MDB2::isError($numcols)) {
  675. return $numcols;
  676. }
  677. for ($column = 0; $column < $numcols; $column++) {
  678. $column_name = @fbsql_field_name($this->result, $column);
  679. $columns[$column_name] = $column;
  680. }
  681. if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  682. $columns = array_change_key_case($columns, $this->db->options['field_case']);
  683. }
  684. return $columns;
  685. }
  686. // }}}
  687. // {{{ numCols()
  688. /**
  689. * Count the number of columns returned by the DBMS in a query result.
  690. *
  691. * @return mixed integer value with the number of columns, a MDB2 error
  692. * on failure
  693. * @access public
  694. */
  695. function numCols()
  696. {
  697. $cols = @fbsql_num_fields($this->result);
  698. if (null === $cols) {
  699. if (false === $this->result) {
  700. return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
  701. 'resultset has already been freed', __FUNCTION__);
  702. }
  703. if (null === $this->result) {
  704. return count($this->types);
  705. }
  706. return $this->db->raiseError(null, null, null,
  707. 'Could not get column count', __FUNCTION__);
  708. }
  709. return $cols;
  710. }
  711. // }}}
  712. // {{{ nextResult()
  713. /**
  714. * Move the internal result pointer to the next available result
  715. *
  716. * @return true on success, false if there is no more result set or an error object on failure
  717. * @access public
  718. */
  719. function nextResult()
  720. {
  721. if (false === $this->result) {
  722. return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
  723. 'resultset has already been freed', __FUNCTION__);
  724. }
  725. if (null === $this->result) {
  726. return false;
  727. }
  728. return @fbsql_next_result($this->result);
  729. }
  730. // }}}
  731. // {{{ free()
  732. /**
  733. * Free the internal resources associated with result.
  734. *
  735. * @return boolean true on success, false if result is invalid
  736. * @access public
  737. */
  738. function free()
  739. {
  740. if (is_resource($this->result) && $this->db->connection) {
  741. $free = @fbsql_free_result($this->result);
  742. if (false === $free) {
  743. return $this->db->raiseError(null, null, null,
  744. 'Could not free result', __FUNCTION__);
  745. }
  746. }
  747. $this->result = false;
  748. return MDB2_OK;
  749. }
  750. }
  751. /**
  752. * MDB2 FrontbaseSQL buffered result driver
  753. *
  754. * @package MDB2
  755. * @category Database
  756. * @author Lukas Smith <smith@pooteeweet.org>
  757. */
  758. class MDB2_BufferedResult_fbsql extends MDB2_Result_fbsql
  759. {
  760. // }}}
  761. // {{{ seek()
  762. /**
  763. * Seek to a specific row in a result set
  764. *
  765. * @param int $rownum number of the row where the data can be found
  766. * @return mixed MDB2_OK on success, a MDB2 error on failure
  767. * @access public
  768. */
  769. function seek($rownum = 0)
  770. {
  771. if ($this->rownum != ($rownum - 1) && !@fbsql_data_seek($this->result, $rownum)) {
  772. if (false === $this->result) {
  773. return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
  774. 'resultset has already been freed', __FUNCTION__);
  775. }
  776. if (null === $this->result) {
  777. return MDB2_OK;
  778. }
  779. return $this->db->raiseError(MDB2_ERROR_INVALID, null, null,
  780. 'tried to seek to an invalid row number ('.$rownum.')', __FUNCTION__);
  781. }
  782. $this->rownum = $rownum - 1;
  783. return MDB2_OK;
  784. }
  785. // }}}
  786. // {{{ valid()
  787. /**
  788. * Check if the end of the result set has been reached
  789. *
  790. * @return mixed true or false on sucess, a MDB2 error on failure
  791. * @access public
  792. */
  793. function valid()
  794. {
  795. $numrows = $this->numRows();
  796. if (MDB2::isError($numrows)) {
  797. return $numrows;
  798. }
  799. return $this->rownum < ($numrows - 1);
  800. }
  801. // }}}
  802. // {{{ numRows()
  803. /**
  804. * Returns the number of rows in a result object
  805. *
  806. * @return mixed MDB2 Error Object or the number of rows
  807. * @access public
  808. */
  809. function numRows()
  810. {
  811. $rows = @fbsql_num_rows($this->result);
  812. if (null === $rows) {
  813. if (false === $this->result) {
  814. return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
  815. 'resultset has already been freed', __FUNCTION__);
  816. }
  817. if (null === $this->result) {
  818. return 0;
  819. }
  820. return $this->db->raiseError(null, null, null,
  821. 'Could not get row count', __FUNCTION__);
  822. }
  823. return $rows;
  824. }
  825. }
  826. /**
  827. * MDB2 FrontbaseSQL statement driver
  828. *
  829. * @package MDB2
  830. * @category Database
  831. * @author Lukas Smith <smith@pooteeweet.org>
  832. */
  833. class MDB2_Statement_fbsql extends MDB2_Statement_Common
  834. {
  835. }
  836. ?>