ibase.php 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * The PEAR DB driver for PHP's interbase extension
  5. * for interacting with Interbase and Firebird databases
  6. *
  7. * While this class works with PHP 4, PHP's InterBase extension is
  8. * unstable in PHP 4. Use PHP 5.
  9. *
  10. * PHP version 5
  11. *
  12. * LICENSE: This source file is subject to version 3.0 of the PHP license
  13. * that is available through the world-wide-web at the following URI:
  14. * http://www.php.net/license/3_0.txt. If you did not receive a copy of
  15. * the PHP License and are unable to obtain it through the web, please
  16. * send a note to license@php.net so we can mail you a copy immediately.
  17. *
  18. * @category Database
  19. * @package DB
  20. * @author Sterling Hughes <sterling@php.net>
  21. * @author Daniel Convissor <danielc@php.net>
  22. * @copyright 1997-2007 The PHP Group
  23. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  24. * @version CVS: $Id$
  25. * @link http://pear.php.net/package/DB
  26. */
  27. /**
  28. * Obtain the DB_common class so it can be extended from
  29. */
  30. require_once 'DB/common.php';
  31. /**
  32. * The methods PEAR DB uses to interact with PHP's interbase extension
  33. * for interacting with Interbase and Firebird databases
  34. *
  35. * These methods overload the ones declared in DB_common.
  36. *
  37. * While this class works with PHP 4, PHP's InterBase extension is
  38. * unstable in PHP 4. Use PHP 5.
  39. *
  40. * NOTICE: limitQuery() only works for Firebird.
  41. *
  42. * @category Database
  43. * @package DB
  44. * @author Sterling Hughes <sterling@php.net>
  45. * @author Daniel Convissor <danielc@php.net>
  46. * @copyright 1997-2007 The PHP Group
  47. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  48. * @version Release: 1.9.2
  49. * @link http://pear.php.net/package/DB
  50. * @since Class became stable in Release 1.7.0
  51. */
  52. class DB_ibase extends DB_common
  53. {
  54. // {{{ properties
  55. /**
  56. * The DB driver type (mysql, oci8, odbc, etc.)
  57. * @var string
  58. */
  59. public $phptype = 'ibase';
  60. /**
  61. * The database syntax variant to be used (db2, access, etc.), if any
  62. * @var string
  63. */
  64. public $dbsyntax = 'ibase';
  65. /**
  66. * The capabilities of this DB implementation
  67. *
  68. * The 'new_link' element contains the PHP version that first provided
  69. * new_link support for this DBMS. Contains false if it's unsupported.
  70. *
  71. * Meaning of the 'limit' element:
  72. * + 'emulate' = emulate with fetch row by number
  73. * + 'alter' = alter the query
  74. * + false = skip rows
  75. *
  76. * NOTE: only firebird supports limit.
  77. *
  78. * @var array
  79. */
  80. public $features = array(
  81. 'limit' => false,
  82. 'new_link' => false,
  83. 'numrows' => 'emulate',
  84. 'pconnect' => true,
  85. 'prepare' => true,
  86. 'ssl' => false,
  87. 'transactions' => true,
  88. );
  89. /**
  90. * A mapping of native error codes to DB error codes
  91. * @var array
  92. */
  93. public $errorcode_map = array(
  94. -104 => DB_ERROR_SYNTAX,
  95. -150 => DB_ERROR_ACCESS_VIOLATION,
  96. -151 => DB_ERROR_ACCESS_VIOLATION,
  97. -155 => DB_ERROR_NOSUCHTABLE,
  98. -157 => DB_ERROR_NOSUCHFIELD,
  99. -158 => DB_ERROR_VALUE_COUNT_ON_ROW,
  100. -170 => DB_ERROR_MISMATCH,
  101. -171 => DB_ERROR_MISMATCH,
  102. -172 => DB_ERROR_INVALID,
  103. // -204 => // Covers too many errors, need to use regex on msg
  104. -205 => DB_ERROR_NOSUCHFIELD,
  105. -206 => DB_ERROR_NOSUCHFIELD,
  106. -208 => DB_ERROR_INVALID,
  107. -219 => DB_ERROR_NOSUCHTABLE,
  108. -297 => DB_ERROR_CONSTRAINT,
  109. -303 => DB_ERROR_INVALID,
  110. -413 => DB_ERROR_INVALID_NUMBER,
  111. -530 => DB_ERROR_CONSTRAINT,
  112. -551 => DB_ERROR_ACCESS_VIOLATION,
  113. -552 => DB_ERROR_ACCESS_VIOLATION,
  114. // -607 => // Covers too many errors, need to use regex on msg
  115. -625 => DB_ERROR_CONSTRAINT_NOT_NULL,
  116. -803 => DB_ERROR_CONSTRAINT,
  117. -804 => DB_ERROR_VALUE_COUNT_ON_ROW,
  118. // -902 => // Covers too many errors, need to use regex on msg
  119. -904 => DB_ERROR_CONNECT_FAILED,
  120. -922 => DB_ERROR_NOSUCHDB,
  121. -923 => DB_ERROR_CONNECT_FAILED,
  122. -924 => DB_ERROR_CONNECT_FAILED
  123. );
  124. /**
  125. * The raw database connection created by PHP
  126. * @var resource
  127. */
  128. public $connection;
  129. /**
  130. * The DSN information for connecting to a database
  131. * @var array
  132. */
  133. public $dsn = array();
  134. /**
  135. * The number of rows affected by a data manipulation query
  136. * @var integer
  137. * @access private
  138. */
  139. public $affected = 0;
  140. /**
  141. * Should data manipulation queries be committed automatically?
  142. * @var bool
  143. * @access private
  144. */
  145. public $autocommit = true;
  146. /**
  147. * The prepared statement handle from the most recently executed statement
  148. *
  149. * {@internal Mainly here because the InterBase/Firebird API is only
  150. * able to retrieve data from result sets if the statemnt handle is
  151. * still in scope.}}
  152. *
  153. * @var resource
  154. */
  155. public $last_stmt;
  156. /**
  157. * Is the given prepared statement a data manipulation query?
  158. * @var array
  159. * @access private
  160. */
  161. public $manip_query = array();
  162. // }}}
  163. // {{{ constructor
  164. /**
  165. * This constructor calls <kbd>parent::__construct()</kbd>
  166. *
  167. * @return void
  168. */
  169. public function __construct()
  170. {
  171. parent::__construct();
  172. }
  173. // }}}
  174. // {{{ connect()
  175. /**
  176. * Connect to the database server, log in and open the database
  177. *
  178. * Don't call this method directly. Use DB::connect() instead.
  179. *
  180. * PEAR DB's ibase driver supports the following extra DSN options:
  181. * + buffers The number of database buffers to allocate for the
  182. * server-side cache.
  183. * + charset The default character set for a database.
  184. * + dialect The default SQL dialect for any statement
  185. * executed within a connection. Defaults to the
  186. * highest one supported by client libraries.
  187. * Functional only with InterBase 6 and up.
  188. * + role Functional only with InterBase 5 and up.
  189. *
  190. * @param array $dsn the data source name
  191. * @param bool $persistent should the connection be persistent?
  192. *
  193. * @return int DB_OK on success. A DB_Error object on failure.
  194. */
  195. public function connect($dsn, $persistent = false)
  196. {
  197. if (!PEAR::loadExtension('interbase')) {
  198. return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
  199. }
  200. $this->dsn = $dsn;
  201. if ($dsn['dbsyntax']) {
  202. $this->dbsyntax = $dsn['dbsyntax'];
  203. }
  204. if ($this->dbsyntax == 'firebird') {
  205. $this->features['limit'] = 'alter';
  206. }
  207. $params = array(
  208. $dsn['hostspec']
  209. ? ($dsn['hostspec'] . ':' . $dsn['database'])
  210. : $dsn['database'],
  211. $dsn['username'] ? $dsn['username'] : null,
  212. $dsn['password'] ? $dsn['password'] : null,
  213. isset($dsn['charset']) ? $dsn['charset'] : null,
  214. isset($dsn['buffers']) ? $dsn['buffers'] : null,
  215. isset($dsn['dialect']) ? $dsn['dialect'] : null,
  216. isset($dsn['role']) ? $dsn['role'] : null,
  217. );
  218. $connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect';
  219. $this->connection = @call_user_func_array($connect_function, $params);
  220. if (!$this->connection) {
  221. return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED);
  222. }
  223. return DB_OK;
  224. }
  225. // }}}
  226. // {{{ disconnect()
  227. /**
  228. * Disconnects from the database server
  229. *
  230. * @return bool TRUE on success, FALSE on failure
  231. */
  232. public function disconnect()
  233. {
  234. $ret = @ibase_close($this->connection);
  235. $this->connection = null;
  236. return $ret;
  237. }
  238. // }}}
  239. // {{{ simpleQuery()
  240. /**
  241. * Sends a query to the database server
  242. *
  243. * @param string the SQL query string
  244. *
  245. * @return mixed + a PHP result resrouce for successful SELECT queries
  246. * + the DB_OK constant for other successful queries
  247. * + a DB_Error object on failure
  248. */
  249. public function simpleQuery($query)
  250. {
  251. $ismanip = $this->_checkManip($query);
  252. $this->last_query = $query;
  253. $query = $this->modifyQuery($query);
  254. $result = @ibase_query($this->connection, $query);
  255. if (!$result) {
  256. return $this->ibaseRaiseError();
  257. }
  258. if ($this->autocommit && $ismanip) {
  259. @ibase_commit($this->connection);
  260. }
  261. if ($ismanip) {
  262. $this->affected = $result;
  263. return DB_OK;
  264. } else {
  265. $this->affected = 0;
  266. return $result;
  267. }
  268. }
  269. // }}}
  270. // {{{ modifyLimitQuery()
  271. /**
  272. * Adds LIMIT clauses to a query string according to current DBMS standards
  273. *
  274. * Only works with Firebird.
  275. *
  276. * @param string $query the query to modify
  277. * @param int $from the row to start to fetching (0 = the first row)
  278. * @param int $count the numbers of rows to fetch
  279. * @param mixed $params array, string or numeric data to be used in
  280. * execution of the statement. Quantity of items
  281. * passed must match quantity of placeholders in
  282. * query: meaning 1 placeholder for non-array
  283. * parameters or 1 placeholder per array element.
  284. *
  285. * @return string the query string with LIMIT clauses added
  286. *
  287. * @access protected
  288. */
  289. public function modifyLimitQuery($query, $from, $count, $params = array())
  290. {
  291. if ($this->dsn['dbsyntax'] == 'firebird') {
  292. $query = preg_replace(
  293. '/^([\s(])*SELECT/i',
  294. "SELECT FIRST $count SKIP $from",
  295. $query
  296. );
  297. }
  298. return $query;
  299. }
  300. // }}}
  301. // {{{ nextResult()
  302. /**
  303. * Move the internal ibase result pointer to the next available result
  304. *
  305. * @param a valid fbsql result resource
  306. *
  307. * @access public
  308. *
  309. * @return true if a result is available otherwise return false
  310. */
  311. public function nextResult($result)
  312. {
  313. return false;
  314. }
  315. // }}}
  316. // {{{ fetchInto()
  317. /**
  318. * Places a row from the result set into the given array
  319. *
  320. * Formating of the array and the data therein are configurable.
  321. * See DB_result::fetchInto() for more information.
  322. *
  323. * This method is not meant to be called directly. Use
  324. * DB_result::fetchInto() instead. It can't be declared "protected"
  325. * because DB_result is a separate object.
  326. *
  327. * @param resource $result the query result resource
  328. * @param array $arr the referenced array to put the data in
  329. * @param int $fetchmode how the resulting array should be indexed
  330. * @param int $rownum the row number to fetch (0 = first row)
  331. *
  332. * @return mixed DB_OK on success, NULL when the end of a result set is
  333. * reached or on failure
  334. *
  335. * @see DB_result::fetchInto()
  336. */
  337. public function fetchInto($result, &$arr, $fetchmode, $rownum = null)
  338. {
  339. if ($rownum !== null) {
  340. return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
  341. }
  342. if ($fetchmode & DB_FETCHMODE_ASSOC) {
  343. if (function_exists('ibase_fetch_assoc')) {
  344. $arr = @ibase_fetch_assoc($result);
  345. } else {
  346. $arr = get_object_vars(ibase_fetch_object($result));
  347. }
  348. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
  349. $arr = array_change_key_case($arr, CASE_LOWER);
  350. }
  351. } else {
  352. $arr = @ibase_fetch_row($result);
  353. }
  354. if (!$arr) {
  355. return null;
  356. }
  357. if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
  358. $this->_rtrimArrayValues($arr);
  359. }
  360. if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
  361. $this->_convertNullArrayValuesToEmpty($arr);
  362. }
  363. return DB_OK;
  364. }
  365. // }}}
  366. // {{{ freeResult()
  367. /**
  368. * Deletes the result set and frees the memory occupied by the result set
  369. *
  370. * This method is not meant to be called directly. Use
  371. * DB_result::free() instead. It can't be declared "protected"
  372. * because DB_result is a separate object.
  373. *
  374. * @param resource $result PHP's query result resource
  375. *
  376. * @return bool TRUE on success, FALSE if $result is invalid
  377. *
  378. * @see DB_result::free()
  379. */
  380. public function freeResult($result)
  381. {
  382. return is_resource($result) ? ibase_free_result($result) : false;
  383. }
  384. // }}}
  385. // {{{ freeQuery()
  386. public function freeQuery($query)
  387. {
  388. return is_resource($query) ? ibase_free_query($query) : false;
  389. }
  390. // }}}
  391. // {{{ affectedRows()
  392. /**
  393. * Determines the number of rows affected by a data maniuplation query
  394. *
  395. * 0 is returned for queries that don't manipulate data.
  396. *
  397. * @return int the number of rows. A DB_Error object on failure.
  398. */
  399. public function affectedRows()
  400. {
  401. if (is_integer($this->affected)) {
  402. return $this->affected;
  403. }
  404. return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
  405. }
  406. // }}}
  407. // {{{ numCols()
  408. /**
  409. * Gets the number of columns in a result set
  410. *
  411. * This method is not meant to be called directly. Use
  412. * DB_result::numCols() instead. It can't be declared "protected"
  413. * because DB_result is a separate object.
  414. *
  415. * @param resource $result PHP's query result resource
  416. *
  417. * @return int the number of columns. A DB_Error object on failure.
  418. *
  419. * @see DB_result::numCols()
  420. */
  421. public function numCols($result)
  422. {
  423. $cols = @ibase_num_fields($result);
  424. if (!$cols) {
  425. return $this->ibaseRaiseError();
  426. }
  427. return $cols;
  428. }
  429. // }}}
  430. // {{{ prepare()
  431. /**
  432. * Prepares a query for multiple execution with execute().
  433. *
  434. * prepare() requires a generic query as string like <code>
  435. * INSERT INTO numbers VALUES (?, ?, ?)
  436. * </code>. The <kbd>?</kbd> characters are placeholders.
  437. *
  438. * Three types of placeholders can be used:
  439. * + <kbd>?</kbd> a quoted scalar value, i.e. strings, integers
  440. * + <kbd>!</kbd> value is inserted 'as is'
  441. * + <kbd>&</kbd> requires a file name. The file's contents get
  442. * inserted into the query (i.e. saving binary
  443. * data in a db)
  444. *
  445. * Use backslashes to escape placeholder characters if you don't want
  446. * them to be interpreted as placeholders. Example: <code>
  447. * "UPDATE foo SET col=? WHERE col='over \& under'"
  448. * </code>
  449. *
  450. * @param string $query query to be prepared
  451. * @return mixed DB statement resource on success. DB_Error on failure.
  452. */
  453. public function prepare($query)
  454. {
  455. $tokens = preg_split(
  456. '/((?<!\\\)[&?!])/',
  457. $query,
  458. -1,
  459. PREG_SPLIT_DELIM_CAPTURE
  460. );
  461. $token = 0;
  462. $types = array();
  463. $newquery = '';
  464. foreach ($tokens as $key => $val) {
  465. switch ($val) {
  466. case '?':
  467. $types[$token++] = DB_PARAM_SCALAR;
  468. break;
  469. case '&':
  470. $types[$token++] = DB_PARAM_OPAQUE;
  471. break;
  472. case '!':
  473. $types[$token++] = DB_PARAM_MISC;
  474. break;
  475. default:
  476. $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val);
  477. $newquery .= $tokens[$key] . '?';
  478. }
  479. }
  480. $newquery = substr($newquery, 0, -1);
  481. $this->last_query = $query;
  482. $newquery = $this->modifyQuery($newquery);
  483. $stmt = @ibase_prepare($this->connection, $newquery);
  484. if ($stmt === false) {
  485. $stmt = $this->ibaseRaiseError();
  486. } else {
  487. $this->prepare_types[(int)$stmt] = $types;
  488. $this->manip_query[(int)$stmt] = DB::isManip($query);
  489. }
  490. return $stmt;
  491. }
  492. // }}}
  493. // {{{ execute()
  494. /**
  495. * Executes a DB statement prepared with prepare().
  496. *
  497. * @param resource $stmt a DB statement resource returned from prepare()
  498. * @param mixed $data array, string or numeric data to be used in
  499. * execution of the statement. Quantity of items
  500. * passed must match quantity of placeholders in
  501. * query: meaning 1 for non-array items or the
  502. * quantity of elements in the array.
  503. * @return object a new DB_Result or a DB_Error when fail
  504. * @see DB_ibase::prepare()
  505. * @access public
  506. */
  507. public function &execute($stmt, $data = array())
  508. {
  509. $data = (array)$data;
  510. $this->last_parameters = $data;
  511. $types = $this->prepare_types[(int)$stmt];
  512. if (count($types) != count($data)) {
  513. $tmp = $this->raiseError(DB_ERROR_MISMATCH);
  514. return $tmp;
  515. }
  516. $i = 0;
  517. foreach ($data as $key => $value) {
  518. if ($types[$i] == DB_PARAM_MISC) {
  519. /*
  520. * ibase doesn't seem to have the ability to pass a
  521. * parameter along unchanged, so strip off quotes from start
  522. * and end, plus turn two single quotes to one single quote,
  523. * in order to avoid the quotes getting escaped by
  524. * ibase and ending up in the database.
  525. */
  526. $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]);
  527. $data[$key] = str_replace("''", "'", $data[$key]);
  528. } elseif ($types[$i] == DB_PARAM_OPAQUE) {
  529. $fp = @fopen($data[$key], 'rb');
  530. if (!$fp) {
  531. $tmp = $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
  532. return $tmp;
  533. }
  534. $data[$key] = fread($fp, filesize($data[$key]));
  535. fclose($fp);
  536. }
  537. $i++;
  538. }
  539. array_unshift($data, $stmt);
  540. $res = call_user_func_array('ibase_execute', $data);
  541. if (!$res) {
  542. $tmp = $this->ibaseRaiseError();
  543. return $tmp;
  544. }
  545. /* XXX need this?
  546. if ($this->autocommit && $this->manip_query[(int)$stmt]) {
  547. @ibase_commit($this->connection);
  548. }*/
  549. $this->last_stmt = $stmt;
  550. if ($this->manip_query[(int)$stmt] || $this->_next_query_manip) {
  551. $this->_last_query_manip = true;
  552. $this->_next_query_manip = false;
  553. $tmp = DB_OK;
  554. } else {
  555. $this->_last_query_manip = false;
  556. $tmp = new DB_result($this, $res);
  557. }
  558. return $tmp;
  559. }
  560. /**
  561. * Frees the internal resources associated with a prepared query
  562. *
  563. * @param resource $stmt the prepared statement's PHP resource
  564. * @param bool $free_resource should the PHP resource be freed too?
  565. * Use false if you need to get data
  566. * from the result set later.
  567. *
  568. * @return bool TRUE on success, FALSE if $result is invalid
  569. *
  570. * @see DB_ibase::prepare()
  571. */
  572. public function freePrepared($stmt, $free_resource = true)
  573. {
  574. if (!is_resource($stmt)) {
  575. return false;
  576. }
  577. if ($free_resource) {
  578. @ibase_free_query($stmt);
  579. }
  580. unset($this->prepare_tokens[(int)$stmt]);
  581. unset($this->prepare_types[(int)$stmt]);
  582. unset($this->manip_query[(int)$stmt]);
  583. return true;
  584. }
  585. // }}}
  586. // {{{ autoCommit()
  587. /**
  588. * Enables or disables automatic commits
  589. *
  590. * @param bool $onoff true turns it on, false turns it off
  591. *
  592. * @return int DB_OK on success. A DB_Error object if the driver
  593. * doesn't support auto-committing transactions.
  594. */
  595. public function autoCommit($onoff = false)
  596. {
  597. $this->autocommit = $onoff ? 1 : 0;
  598. return DB_OK;
  599. }
  600. // }}}
  601. // {{{ commit()
  602. /**
  603. * Commits the current transaction
  604. *
  605. * @return int DB_OK on success. A DB_Error object on failure.
  606. */
  607. public function commit()
  608. {
  609. return @ibase_commit($this->connection);
  610. }
  611. // }}}
  612. // {{{ rollback()
  613. /**
  614. * Reverts the current transaction
  615. *
  616. * @return int DB_OK on success. A DB_Error object on failure.
  617. */
  618. public function rollback()
  619. {
  620. return @ibase_rollback($this->connection);
  621. }
  622. // }}}
  623. // {{{ transactionInit()
  624. public function transactionInit($trans_args = 0)
  625. {
  626. return $trans_args
  627. ? @ibase_trans($trans_args, $this->connection)
  628. : @ibase_trans();
  629. }
  630. // }}}
  631. // {{{ nextId()
  632. /**
  633. * Returns the next free id in a sequence
  634. *
  635. * @param string $seq_name name of the sequence
  636. * @param boolean $ondemand when true, the seqence is automatically
  637. * created if it does not exist
  638. *
  639. * @return int the next id number in the sequence.
  640. * A DB_Error object on failure.
  641. *
  642. * @see DB_common::nextID(), DB_common::getSequenceName(),
  643. * DB_ibase::createSequence(), DB_ibase::dropSequence()
  644. */
  645. public function nextId($seq_name, $ondemand = true)
  646. {
  647. $sqn = strtoupper($this->getSequenceName($seq_name));
  648. $repeat = 0;
  649. do {
  650. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  651. $result = $this->query("SELECT GEN_ID(${sqn}, 1) "
  652. . 'FROM RDB$GENERATORS '
  653. . "WHERE RDB\$GENERATOR_NAME='${sqn}'");
  654. $this->popErrorHandling();
  655. if ($ondemand && DB::isError($result)) {
  656. $repeat = 1;
  657. $result = $this->createSequence($seq_name);
  658. if (DB::isError($result)) {
  659. return $result;
  660. }
  661. } else {
  662. $repeat = 0;
  663. }
  664. } while ($repeat);
  665. if (DB::isError($result)) {
  666. return $this->raiseError($result);
  667. }
  668. $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
  669. $result->free();
  670. return $arr[0];
  671. }
  672. // }}}
  673. // {{{ createSequence()
  674. /**
  675. * Creates a new sequence
  676. *
  677. * @param string $seq_name name of the new sequence
  678. *
  679. * @return int DB_OK on success. A DB_Error object on failure.
  680. *
  681. * @see DB_common::createSequence(), DB_common::getSequenceName(),
  682. * DB_ibase::nextID(), DB_ibase::dropSequence()
  683. */
  684. public function createSequence($seq_name)
  685. {
  686. $sqn = strtoupper($this->getSequenceName($seq_name));
  687. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  688. $result = $this->query("CREATE GENERATOR ${sqn}");
  689. $this->popErrorHandling();
  690. return $result;
  691. }
  692. // }}}
  693. // {{{ dropSequence()
  694. /**
  695. * Deletes a sequence
  696. *
  697. * @param string $seq_name name of the sequence to be deleted
  698. *
  699. * @return int DB_OK on success. A DB_Error object on failure.
  700. *
  701. * @see DB_common::dropSequence(), DB_common::getSequenceName(),
  702. * DB_ibase::nextID(), DB_ibase::createSequence()
  703. */
  704. public function dropSequence($seq_name)
  705. {
  706. return $this->query('DELETE FROM RDB$GENERATORS '
  707. . "WHERE RDB\$GENERATOR_NAME='"
  708. . strtoupper($this->getSequenceName($seq_name))
  709. . "'");
  710. }
  711. // }}}
  712. // {{{ _ibaseFieldFlags()
  713. /**
  714. * Get the column's flags
  715. *
  716. * Supports "primary_key", "unique_key", "not_null", "default",
  717. * "computed" and "blob".
  718. *
  719. * @param string $field_name the name of the field
  720. * @param string $table_name the name of the table
  721. *
  722. * @return string the flags
  723. *
  724. * @access private
  725. */
  726. public function _ibaseFieldFlags($field_name, $table_name)
  727. {
  728. $sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE'
  729. .' FROM RDB$INDEX_SEGMENTS I'
  730. .' JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME'
  731. .' WHERE I.RDB$FIELD_NAME=\'' . $field_name . '\''
  732. .' AND UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\'';
  733. $result = @ibase_query($this->connection, $sql);
  734. if (!$result) {
  735. return $this->ibaseRaiseError();
  736. }
  737. $flags = '';
  738. if ($obj = @ibase_fetch_object($result)) {
  739. @ibase_free_result($result);
  740. if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'PRIMARY KEY') {
  741. $flags .= 'primary_key ';
  742. }
  743. if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'UNIQUE') {
  744. $flags .= 'unique_key ';
  745. }
  746. }
  747. $sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,'
  748. .' R.RDB$DEFAULT_SOURCE AS DSOURCE,'
  749. .' F.RDB$FIELD_TYPE AS FTYPE,'
  750. .' F.RDB$COMPUTED_SOURCE AS CSOURCE'
  751. .' FROM RDB$RELATION_FIELDS R '
  752. .' JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME'
  753. .' WHERE UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\''
  754. .' AND R.RDB$FIELD_NAME=\'' . $field_name . '\'';
  755. $result = @ibase_query($this->connection, $sql);
  756. if (!$result) {
  757. return $this->ibaseRaiseError();
  758. }
  759. if ($obj = @ibase_fetch_object($result)) {
  760. @ibase_free_result($result);
  761. if (isset($obj->NFLAG)) {
  762. $flags .= 'not_null ';
  763. }
  764. if (isset($obj->DSOURCE)) {
  765. $flags .= 'default ';
  766. }
  767. if (isset($obj->CSOURCE)) {
  768. $flags .= 'computed ';
  769. }
  770. if (isset($obj->FTYPE) && $obj->FTYPE == 261) {
  771. $flags .= 'blob ';
  772. }
  773. }
  774. return trim($flags);
  775. }
  776. // }}}
  777. // {{{ ibaseRaiseError()
  778. /**
  779. * Produces a DB_Error object regarding the current problem
  780. *
  781. * @param int $errno if the error is being manually raised pass a
  782. * DB_ERROR* constant here. If this isn't passed
  783. * the error information gathered from the DBMS.
  784. *
  785. * @return object the DB_Error object
  786. *
  787. * @see DB_common::raiseError(),
  788. * DB_ibase::errorNative(), DB_ibase::errorCode()
  789. */
  790. public function &ibaseRaiseError($errno = null)
  791. {
  792. if ($errno === null) {
  793. $errno = $this->errorCode($this->errorNative());
  794. }
  795. $tmp = $this->raiseError($errno, null, null, null, @ibase_errmsg());
  796. return $tmp;
  797. }
  798. // }}}
  799. // {{{ errorNative()
  800. /**
  801. * Gets the DBMS' native error code produced by the last query
  802. *
  803. * @return int the DBMS' error code. NULL if there is no error code.
  804. *
  805. * @since Method available since Release 1.7.0
  806. */
  807. public function errorNative()
  808. {
  809. if (function_exists('ibase_errcode')) {
  810. return @ibase_errcode();
  811. }
  812. if (preg_match(
  813. '/^Dynamic SQL Error SQL error code = ([0-9-]+)/i',
  814. @ibase_errmsg(),
  815. $m
  816. )) {
  817. return (int)$m[1];
  818. }
  819. return null;
  820. }
  821. // }}}
  822. // {{{ errorCode()
  823. /**
  824. * Maps native error codes to DB's portable ones
  825. *
  826. * @param int $nativecode the error code returned by the DBMS
  827. *
  828. * @return int the portable DB error code. Return DB_ERROR if the
  829. * current driver doesn't have a mapping for the
  830. * $nativecode submitted.
  831. *
  832. * @since Method available since Release 1.7.0
  833. */
  834. public function errorCode($nativecode = null)
  835. {
  836. if (isset($this->errorcode_map[$nativecode])) {
  837. return $this->errorcode_map[$nativecode];
  838. }
  839. static $error_regexps;
  840. if (!isset($error_regexps)) {
  841. $error_regexps = array(
  842. '/generator .* is not defined/'
  843. => DB_ERROR_SYNTAX, // for compat. w ibase_errcode()
  844. '/violation of [\w ]+ constraint/i'
  845. => DB_ERROR_CONSTRAINT,
  846. '/table.*(not exist|not found|unknown)/i'
  847. => DB_ERROR_NOSUCHTABLE,
  848. '/table .* already exists/i'
  849. => DB_ERROR_ALREADY_EXISTS,
  850. '/unsuccessful metadata update .* failed attempt to store duplicate value/i'
  851. => DB_ERROR_ALREADY_EXISTS,
  852. '/unsuccessful metadata update .* not found/i'
  853. => DB_ERROR_NOT_FOUND,
  854. '/validation error for column .* value "\*\*\* null/i'
  855. => DB_ERROR_CONSTRAINT_NOT_NULL,
  856. '/conversion error from string/i'
  857. => DB_ERROR_INVALID_NUMBER,
  858. '/no permission for/i'
  859. => DB_ERROR_ACCESS_VIOLATION,
  860. '/arithmetic exception, numeric overflow, or string truncation/i'
  861. => DB_ERROR_INVALID,
  862. '/feature is not supported/i'
  863. => DB_ERROR_NOT_CAPABLE,
  864. );
  865. }
  866. $errormsg = @ibase_errmsg();
  867. foreach ($error_regexps as $regexp => $code) {
  868. if (preg_match($regexp, $errormsg)) {
  869. return $code;
  870. }
  871. }
  872. return DB_ERROR;
  873. }
  874. // }}}
  875. // {{{ tableInfo()
  876. /**
  877. * Returns information about a table or a result set
  878. *
  879. * NOTE: only supports 'table' and 'flags' if <var>$result</var>
  880. * is a table name.
  881. *
  882. * @param object|string $result DB_result object from a query or a
  883. * string containing the name of a table.
  884. * While this also accepts a query result
  885. * resource identifier, this behavior is
  886. * deprecated.
  887. * @param int $mode a valid tableInfo mode
  888. *
  889. * @return array an associative array with the information requested.
  890. * A DB_Error object on failure.
  891. *
  892. * @see DB_common::tableInfo()
  893. */
  894. public function tableInfo($result, $mode = null)
  895. {
  896. if (is_string($result)) {
  897. /*
  898. * Probably received a table name.
  899. * Create a result resource identifier.
  900. */
  901. $id = @ibase_query(
  902. $this->connection,
  903. "SELECT * FROM $result WHERE 1=0"
  904. );
  905. $got_string = true;
  906. } elseif (isset($result->result)) {
  907. /*
  908. * Probably received a result object.
  909. * Extract the result resource identifier.
  910. */
  911. $id = $result->result;
  912. $got_string = false;
  913. } else {
  914. /*
  915. * Probably received a result resource identifier.
  916. * Copy it.
  917. * Deprecated. Here for compatibility only.
  918. */
  919. $id = $result;
  920. $got_string = false;
  921. }
  922. if (!is_resource($id)) {
  923. return $this->ibaseRaiseError(DB_ERROR_NEED_MORE_DATA);
  924. }
  925. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
  926. $case_func = 'strtolower';
  927. } else {
  928. $case_func = 'strval';
  929. }
  930. $count = @ibase_num_fields($id);
  931. $res = array();
  932. if ($mode) {
  933. $res['num_fields'] = $count;
  934. }
  935. for ($i = 0; $i < $count; $i++) {
  936. $info = @ibase_field_info($id, $i);
  937. $res[$i] = array(
  938. 'table' => $got_string ? $case_func($result) : '',
  939. 'name' => $case_func($info['name']),
  940. 'type' => $info['type'],
  941. 'len' => $info['length'],
  942. 'flags' => ($got_string)
  943. ? $this->_ibaseFieldFlags($info['name'], $result)
  944. : '',
  945. );
  946. if ($mode & DB_TABLEINFO_ORDER) {
  947. $res['order'][$res[$i]['name']] = $i;
  948. }
  949. if ($mode & DB_TABLEINFO_ORDERTABLE) {
  950. $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  951. }
  952. }
  953. // free the result only if we were called on a table
  954. if ($got_string) {
  955. @ibase_free_result($id);
  956. }
  957. return $res;
  958. }
  959. // }}}
  960. // {{{ getSpecialQuery()
  961. /**
  962. * Obtains the query string needed for listing a given type of objects
  963. *
  964. * @param string $type the kind of objects you want to retrieve
  965. *
  966. * @return string the SQL query string or null if the driver doesn't
  967. * support the object type requested
  968. *
  969. * @access protected
  970. * @see DB_common::getListOf()
  971. */
  972. public function getSpecialQuery($type)
  973. {
  974. switch ($type) {
  975. case 'tables':
  976. return 'SELECT DISTINCT R.RDB$RELATION_NAME FROM '
  977. . 'RDB$RELATION_FIELDS R WHERE R.RDB$SYSTEM_FLAG=0';
  978. case 'views':
  979. return 'SELECT DISTINCT RDB$VIEW_NAME from RDB$VIEW_RELATIONS';
  980. case 'users':
  981. return 'SELECT DISTINCT RDB$USER FROM RDB$USER_PRIVILEGES';
  982. default:
  983. return null;
  984. }
  985. }
  986. // }}}
  987. }
  988. /*
  989. * Local variables:
  990. * tab-width: 4
  991. * c-basic-offset: 4
  992. * End:
  993. */