ibase.php 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083
  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.8.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. var $phptype = 'ibase';
  60. /**
  61. * The database syntax variant to be used (db2, access, etc.), if any
  62. * @var string
  63. */
  64. var $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. var $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. var $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. var $connection;
  129. /**
  130. * The DSN information for connecting to a database
  131. * @var array
  132. */
  133. var $dsn = array();
  134. /**
  135. * The number of rows affected by a data manipulation query
  136. * @var integer
  137. * @access private
  138. */
  139. var $affected = 0;
  140. /**
  141. * Should data manipulation queries be committed automatically?
  142. * @var bool
  143. * @access private
  144. */
  145. var $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. var $last_stmt;
  156. /**
  157. * Is the given prepared statement a data manipulation query?
  158. * @var array
  159. * @access private
  160. */
  161. var $manip_query = array();
  162. // }}}
  163. // {{{ constructor
  164. /**
  165. * This constructor calls <kbd>$this->DB_common()</kbd>
  166. *
  167. * @return void
  168. */
  169. function DB_ibase()
  170. {
  171. $this->DB_common();
  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. 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. 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. 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. function modifyLimitQuery($query, $from, $count, $params = array())
  290. {
  291. if ($this->dsn['dbsyntax'] == 'firebird') {
  292. $query = preg_replace('/^([\s(])*SELECT/i',
  293. "SELECT FIRST $count SKIP $from", $query);
  294. }
  295. return $query;
  296. }
  297. // }}}
  298. // {{{ nextResult()
  299. /**
  300. * Move the internal ibase result pointer to the next available result
  301. *
  302. * @param a valid fbsql result resource
  303. *
  304. * @access public
  305. *
  306. * @return true if a result is available otherwise return false
  307. */
  308. function nextResult($result)
  309. {
  310. return false;
  311. }
  312. // }}}
  313. // {{{ fetchInto()
  314. /**
  315. * Places a row from the result set into the given array
  316. *
  317. * Formating of the array and the data therein are configurable.
  318. * See DB_result::fetchInto() for more information.
  319. *
  320. * This method is not meant to be called directly. Use
  321. * DB_result::fetchInto() instead. It can't be declared "protected"
  322. * because DB_result is a separate object.
  323. *
  324. * @param resource $result the query result resource
  325. * @param array $arr the referenced array to put the data in
  326. * @param int $fetchmode how the resulting array should be indexed
  327. * @param int $rownum the row number to fetch (0 = first row)
  328. *
  329. * @return mixed DB_OK on success, NULL when the end of a result set is
  330. * reached or on failure
  331. *
  332. * @see DB_result::fetchInto()
  333. */
  334. function fetchInto($result, &$arr, $fetchmode, $rownum = null)
  335. {
  336. if ($rownum !== null) {
  337. return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
  338. }
  339. if ($fetchmode & DB_FETCHMODE_ASSOC) {
  340. if (function_exists('ibase_fetch_assoc')) {
  341. $arr = @ibase_fetch_assoc($result);
  342. } else {
  343. $arr = get_object_vars(ibase_fetch_object($result));
  344. }
  345. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
  346. $arr = array_change_key_case($arr, CASE_LOWER);
  347. }
  348. } else {
  349. $arr = @ibase_fetch_row($result);
  350. }
  351. if (!$arr) {
  352. return null;
  353. }
  354. if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
  355. $this->_rtrimArrayValues($arr);
  356. }
  357. if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
  358. $this->_convertNullArrayValuesToEmpty($arr);
  359. }
  360. return DB_OK;
  361. }
  362. // }}}
  363. // {{{ freeResult()
  364. /**
  365. * Deletes the result set and frees the memory occupied by the result set
  366. *
  367. * This method is not meant to be called directly. Use
  368. * DB_result::free() instead. It can't be declared "protected"
  369. * because DB_result is a separate object.
  370. *
  371. * @param resource $result PHP's query result resource
  372. *
  373. * @return bool TRUE on success, FALSE if $result is invalid
  374. *
  375. * @see DB_result::free()
  376. */
  377. function freeResult($result)
  378. {
  379. return is_resource($result) ? ibase_free_result($result) : false;
  380. }
  381. // }}}
  382. // {{{ freeQuery()
  383. function freeQuery($query)
  384. {
  385. return is_resource($query) ? ibase_free_query($query) : false;
  386. }
  387. // }}}
  388. // {{{ affectedRows()
  389. /**
  390. * Determines the number of rows affected by a data maniuplation query
  391. *
  392. * 0 is returned for queries that don't manipulate data.
  393. *
  394. * @return int the number of rows. A DB_Error object on failure.
  395. */
  396. function affectedRows()
  397. {
  398. if (is_integer($this->affected)) {
  399. return $this->affected;
  400. }
  401. return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
  402. }
  403. // }}}
  404. // {{{ numCols()
  405. /**
  406. * Gets the number of columns in a result set
  407. *
  408. * This method is not meant to be called directly. Use
  409. * DB_result::numCols() instead. It can't be declared "protected"
  410. * because DB_result is a separate object.
  411. *
  412. * @param resource $result PHP's query result resource
  413. *
  414. * @return int the number of columns. A DB_Error object on failure.
  415. *
  416. * @see DB_result::numCols()
  417. */
  418. function numCols($result)
  419. {
  420. $cols = @ibase_num_fields($result);
  421. if (!$cols) {
  422. return $this->ibaseRaiseError();
  423. }
  424. return $cols;
  425. }
  426. // }}}
  427. // {{{ prepare()
  428. /**
  429. * Prepares a query for multiple execution with execute().
  430. *
  431. * prepare() requires a generic query as string like <code>
  432. * INSERT INTO numbers VALUES (?, ?, ?)
  433. * </code>. The <kbd>?</kbd> characters are placeholders.
  434. *
  435. * Three types of placeholders can be used:
  436. * + <kbd>?</kbd> a quoted scalar value, i.e. strings, integers
  437. * + <kbd>!</kbd> value is inserted 'as is'
  438. * + <kbd>&</kbd> requires a file name. The file's contents get
  439. * inserted into the query (i.e. saving binary
  440. * data in a db)
  441. *
  442. * Use backslashes to escape placeholder characters if you don't want
  443. * them to be interpreted as placeholders. Example: <code>
  444. * "UPDATE foo SET col=? WHERE col='over \& under'"
  445. * </code>
  446. *
  447. * @param string $query query to be prepared
  448. * @return mixed DB statement resource on success. DB_Error on failure.
  449. */
  450. function prepare($query)
  451. {
  452. $tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1,
  453. PREG_SPLIT_DELIM_CAPTURE);
  454. $token = 0;
  455. $types = array();
  456. $newquery = '';
  457. foreach ($tokens as $key => $val) {
  458. switch ($val) {
  459. case '?':
  460. $types[$token++] = DB_PARAM_SCALAR;
  461. break;
  462. case '&':
  463. $types[$token++] = DB_PARAM_OPAQUE;
  464. break;
  465. case '!':
  466. $types[$token++] = DB_PARAM_MISC;
  467. break;
  468. default:
  469. $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val);
  470. $newquery .= $tokens[$key] . '?';
  471. }
  472. }
  473. $newquery = substr($newquery, 0, -1);
  474. $this->last_query = $query;
  475. $newquery = $this->modifyQuery($newquery);
  476. $stmt = @ibase_prepare($this->connection, $newquery);
  477. if ($stmt === false) {
  478. $stmt = $this->ibaseRaiseError();
  479. } else {
  480. $this->prepare_types[(int)$stmt] = $types;
  481. $this->manip_query[(int)$stmt] = DB::isManip($query);
  482. }
  483. return $stmt;
  484. }
  485. // }}}
  486. // {{{ execute()
  487. /**
  488. * Executes a DB statement prepared with prepare().
  489. *
  490. * @param resource $stmt a DB statement resource returned from prepare()
  491. * @param mixed $data array, string or numeric data to be used in
  492. * execution of the statement. Quantity of items
  493. * passed must match quantity of placeholders in
  494. * query: meaning 1 for non-array items or the
  495. * quantity of elements in the array.
  496. * @return object a new DB_Result or a DB_Error when fail
  497. * @see DB_ibase::prepare()
  498. * @access public
  499. */
  500. function &execute($stmt, $data = array())
  501. {
  502. $data = (array)$data;
  503. $this->last_parameters = $data;
  504. $types = $this->prepare_types[(int)$stmt];
  505. if (count($types) != count($data)) {
  506. $tmp = $this->raiseError(DB_ERROR_MISMATCH);
  507. return $tmp;
  508. }
  509. $i = 0;
  510. foreach ($data as $key => $value) {
  511. if ($types[$i] == DB_PARAM_MISC) {
  512. /*
  513. * ibase doesn't seem to have the ability to pass a
  514. * parameter along unchanged, so strip off quotes from start
  515. * and end, plus turn two single quotes to one single quote,
  516. * in order to avoid the quotes getting escaped by
  517. * ibase and ending up in the database.
  518. */
  519. $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]);
  520. $data[$key] = str_replace("''", "'", $data[$key]);
  521. } elseif ($types[$i] == DB_PARAM_OPAQUE) {
  522. $fp = @fopen($data[$key], 'rb');
  523. if (!$fp) {
  524. $tmp = $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
  525. return $tmp;
  526. }
  527. $data[$key] = fread($fp, filesize($data[$key]));
  528. fclose($fp);
  529. }
  530. $i++;
  531. }
  532. array_unshift($data, $stmt);
  533. $res = call_user_func_array('ibase_execute', $data);
  534. if (!$res) {
  535. $tmp = $this->ibaseRaiseError();
  536. return $tmp;
  537. }
  538. /* XXX need this?
  539. if ($this->autocommit && $this->manip_query[(int)$stmt]) {
  540. @ibase_commit($this->connection);
  541. }*/
  542. $this->last_stmt = $stmt;
  543. if ($this->manip_query[(int)$stmt] || $this->_next_query_manip) {
  544. $this->_last_query_manip = true;
  545. $this->_next_query_manip = false;
  546. $tmp = DB_OK;
  547. } else {
  548. $this->_last_query_manip = false;
  549. $tmp = new DB_result($this, $res);
  550. }
  551. return $tmp;
  552. }
  553. /**
  554. * Frees the internal resources associated with a prepared query
  555. *
  556. * @param resource $stmt the prepared statement's PHP resource
  557. * @param bool $free_resource should the PHP resource be freed too?
  558. * Use false if you need to get data
  559. * from the result set later.
  560. *
  561. * @return bool TRUE on success, FALSE if $result is invalid
  562. *
  563. * @see DB_ibase::prepare()
  564. */
  565. function freePrepared($stmt, $free_resource = true)
  566. {
  567. if (!is_resource($stmt)) {
  568. return false;
  569. }
  570. if ($free_resource) {
  571. @ibase_free_query($stmt);
  572. }
  573. unset($this->prepare_tokens[(int)$stmt]);
  574. unset($this->prepare_types[(int)$stmt]);
  575. unset($this->manip_query[(int)$stmt]);
  576. return true;
  577. }
  578. // }}}
  579. // {{{ autoCommit()
  580. /**
  581. * Enables or disables automatic commits
  582. *
  583. * @param bool $onoff true turns it on, false turns it off
  584. *
  585. * @return int DB_OK on success. A DB_Error object if the driver
  586. * doesn't support auto-committing transactions.
  587. */
  588. function autoCommit($onoff = false)
  589. {
  590. $this->autocommit = $onoff ? 1 : 0;
  591. return DB_OK;
  592. }
  593. // }}}
  594. // {{{ commit()
  595. /**
  596. * Commits the current transaction
  597. *
  598. * @return int DB_OK on success. A DB_Error object on failure.
  599. */
  600. function commit()
  601. {
  602. return @ibase_commit($this->connection);
  603. }
  604. // }}}
  605. // {{{ rollback()
  606. /**
  607. * Reverts the current transaction
  608. *
  609. * @return int DB_OK on success. A DB_Error object on failure.
  610. */
  611. function rollback()
  612. {
  613. return @ibase_rollback($this->connection);
  614. }
  615. // }}}
  616. // {{{ transactionInit()
  617. function transactionInit($trans_args = 0)
  618. {
  619. return $trans_args
  620. ? @ibase_trans($trans_args, $this->connection)
  621. : @ibase_trans();
  622. }
  623. // }}}
  624. // {{{ nextId()
  625. /**
  626. * Returns the next free id in a sequence
  627. *
  628. * @param string $seq_name name of the sequence
  629. * @param boolean $ondemand when true, the seqence is automatically
  630. * created if it does not exist
  631. *
  632. * @return int the next id number in the sequence.
  633. * A DB_Error object on failure.
  634. *
  635. * @see DB_common::nextID(), DB_common::getSequenceName(),
  636. * DB_ibase::createSequence(), DB_ibase::dropSequence()
  637. */
  638. function nextId($seq_name, $ondemand = true)
  639. {
  640. $sqn = strtoupper($this->getSequenceName($seq_name));
  641. $repeat = 0;
  642. do {
  643. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  644. $result = $this->query("SELECT GEN_ID(${sqn}, 1) "
  645. . 'FROM RDB$GENERATORS '
  646. . "WHERE RDB\$GENERATOR_NAME='${sqn}'");
  647. $this->popErrorHandling();
  648. if ($ondemand && DB::isError($result)) {
  649. $repeat = 1;
  650. $result = $this->createSequence($seq_name);
  651. if (DB::isError($result)) {
  652. return $result;
  653. }
  654. } else {
  655. $repeat = 0;
  656. }
  657. } while ($repeat);
  658. if (DB::isError($result)) {
  659. return $this->raiseError($result);
  660. }
  661. $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
  662. $result->free();
  663. return $arr[0];
  664. }
  665. // }}}
  666. // {{{ createSequence()
  667. /**
  668. * Creates a new sequence
  669. *
  670. * @param string $seq_name name of the new sequence
  671. *
  672. * @return int DB_OK on success. A DB_Error object on failure.
  673. *
  674. * @see DB_common::createSequence(), DB_common::getSequenceName(),
  675. * DB_ibase::nextID(), DB_ibase::dropSequence()
  676. */
  677. function createSequence($seq_name)
  678. {
  679. $sqn = strtoupper($this->getSequenceName($seq_name));
  680. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  681. $result = $this->query("CREATE GENERATOR ${sqn}");
  682. $this->popErrorHandling();
  683. return $result;
  684. }
  685. // }}}
  686. // {{{ dropSequence()
  687. /**
  688. * Deletes a sequence
  689. *
  690. * @param string $seq_name name of the sequence to be deleted
  691. *
  692. * @return int DB_OK on success. A DB_Error object on failure.
  693. *
  694. * @see DB_common::dropSequence(), DB_common::getSequenceName(),
  695. * DB_ibase::nextID(), DB_ibase::createSequence()
  696. */
  697. function dropSequence($seq_name)
  698. {
  699. return $this->query('DELETE FROM RDB$GENERATORS '
  700. . "WHERE RDB\$GENERATOR_NAME='"
  701. . strtoupper($this->getSequenceName($seq_name))
  702. . "'");
  703. }
  704. // }}}
  705. // {{{ _ibaseFieldFlags()
  706. /**
  707. * Get the column's flags
  708. *
  709. * Supports "primary_key", "unique_key", "not_null", "default",
  710. * "computed" and "blob".
  711. *
  712. * @param string $field_name the name of the field
  713. * @param string $table_name the name of the table
  714. *
  715. * @return string the flags
  716. *
  717. * @access private
  718. */
  719. function _ibaseFieldFlags($field_name, $table_name)
  720. {
  721. $sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE'
  722. .' FROM RDB$INDEX_SEGMENTS I'
  723. .' JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME'
  724. .' WHERE I.RDB$FIELD_NAME=\'' . $field_name . '\''
  725. .' AND UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\'';
  726. $result = @ibase_query($this->connection, $sql);
  727. if (!$result) {
  728. return $this->ibaseRaiseError();
  729. }
  730. $flags = '';
  731. if ($obj = @ibase_fetch_object($result)) {
  732. @ibase_free_result($result);
  733. if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'PRIMARY KEY') {
  734. $flags .= 'primary_key ';
  735. }
  736. if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'UNIQUE') {
  737. $flags .= 'unique_key ';
  738. }
  739. }
  740. $sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,'
  741. .' R.RDB$DEFAULT_SOURCE AS DSOURCE,'
  742. .' F.RDB$FIELD_TYPE AS FTYPE,'
  743. .' F.RDB$COMPUTED_SOURCE AS CSOURCE'
  744. .' FROM RDB$RELATION_FIELDS R '
  745. .' JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME'
  746. .' WHERE UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\''
  747. .' AND R.RDB$FIELD_NAME=\'' . $field_name . '\'';
  748. $result = @ibase_query($this->connection, $sql);
  749. if (!$result) {
  750. return $this->ibaseRaiseError();
  751. }
  752. if ($obj = @ibase_fetch_object($result)) {
  753. @ibase_free_result($result);
  754. if (isset($obj->NFLAG)) {
  755. $flags .= 'not_null ';
  756. }
  757. if (isset($obj->DSOURCE)) {
  758. $flags .= 'default ';
  759. }
  760. if (isset($obj->CSOURCE)) {
  761. $flags .= 'computed ';
  762. }
  763. if (isset($obj->FTYPE) && $obj->FTYPE == 261) {
  764. $flags .= 'blob ';
  765. }
  766. }
  767. return trim($flags);
  768. }
  769. // }}}
  770. // {{{ ibaseRaiseError()
  771. /**
  772. * Produces a DB_Error object regarding the current problem
  773. *
  774. * @param int $errno if the error is being manually raised pass a
  775. * DB_ERROR* constant here. If this isn't passed
  776. * the error information gathered from the DBMS.
  777. *
  778. * @return object the DB_Error object
  779. *
  780. * @see DB_common::raiseError(),
  781. * DB_ibase::errorNative(), DB_ibase::errorCode()
  782. */
  783. function &ibaseRaiseError($errno = null)
  784. {
  785. if ($errno === null) {
  786. $errno = $this->errorCode($this->errorNative());
  787. }
  788. $tmp = $this->raiseError($errno, null, null, null, @ibase_errmsg());
  789. return $tmp;
  790. }
  791. // }}}
  792. // {{{ errorNative()
  793. /**
  794. * Gets the DBMS' native error code produced by the last query
  795. *
  796. * @return int the DBMS' error code. NULL if there is no error code.
  797. *
  798. * @since Method available since Release 1.7.0
  799. */
  800. function errorNative()
  801. {
  802. if (function_exists('ibase_errcode')) {
  803. return @ibase_errcode();
  804. }
  805. if (preg_match('/^Dynamic SQL Error SQL error code = ([0-9-]+)/i',
  806. @ibase_errmsg(), $m)) {
  807. return (int)$m[1];
  808. }
  809. return null;
  810. }
  811. // }}}
  812. // {{{ errorCode()
  813. /**
  814. * Maps native error codes to DB's portable ones
  815. *
  816. * @param int $nativecode the error code returned by the DBMS
  817. *
  818. * @return int the portable DB error code. Return DB_ERROR if the
  819. * current driver doesn't have a mapping for the
  820. * $nativecode submitted.
  821. *
  822. * @since Method available since Release 1.7.0
  823. */
  824. function errorCode($nativecode = null)
  825. {
  826. if (isset($this->errorcode_map[$nativecode])) {
  827. return $this->errorcode_map[$nativecode];
  828. }
  829. static $error_regexps;
  830. if (!isset($error_regexps)) {
  831. $error_regexps = array(
  832. '/generator .* is not defined/'
  833. => DB_ERROR_SYNTAX, // for compat. w ibase_errcode()
  834. '/violation of [\w ]+ constraint/i'
  835. => DB_ERROR_CONSTRAINT,
  836. '/table.*(not exist|not found|unknown)/i'
  837. => DB_ERROR_NOSUCHTABLE,
  838. '/table .* already exists/i'
  839. => DB_ERROR_ALREADY_EXISTS,
  840. '/unsuccessful metadata update .* failed attempt to store duplicate value/i'
  841. => DB_ERROR_ALREADY_EXISTS,
  842. '/unsuccessful metadata update .* not found/i'
  843. => DB_ERROR_NOT_FOUND,
  844. '/validation error for column .* value "\*\*\* null/i'
  845. => DB_ERROR_CONSTRAINT_NOT_NULL,
  846. '/conversion error from string/i'
  847. => DB_ERROR_INVALID_NUMBER,
  848. '/no permission for/i'
  849. => DB_ERROR_ACCESS_VIOLATION,
  850. '/arithmetic exception, numeric overflow, or string truncation/i'
  851. => DB_ERROR_INVALID,
  852. '/feature is not supported/i'
  853. => DB_ERROR_NOT_CAPABLE,
  854. );
  855. }
  856. $errormsg = @ibase_errmsg();
  857. foreach ($error_regexps as $regexp => $code) {
  858. if (preg_match($regexp, $errormsg)) {
  859. return $code;
  860. }
  861. }
  862. return DB_ERROR;
  863. }
  864. // }}}
  865. // {{{ tableInfo()
  866. /**
  867. * Returns information about a table or a result set
  868. *
  869. * NOTE: only supports 'table' and 'flags' if <var>$result</var>
  870. * is a table name.
  871. *
  872. * @param object|string $result DB_result object from a query or a
  873. * string containing the name of a table.
  874. * While this also accepts a query result
  875. * resource identifier, this behavior is
  876. * deprecated.
  877. * @param int $mode a valid tableInfo mode
  878. *
  879. * @return array an associative array with the information requested.
  880. * A DB_Error object on failure.
  881. *
  882. * @see DB_common::tableInfo()
  883. */
  884. function tableInfo($result, $mode = null)
  885. {
  886. if (is_string($result)) {
  887. /*
  888. * Probably received a table name.
  889. * Create a result resource identifier.
  890. */
  891. $id = @ibase_query($this->connection,
  892. "SELECT * FROM $result WHERE 1=0");
  893. $got_string = true;
  894. } elseif (isset($result->result)) {
  895. /*
  896. * Probably received a result object.
  897. * Extract the result resource identifier.
  898. */
  899. $id = $result->result;
  900. $got_string = false;
  901. } else {
  902. /*
  903. * Probably received a result resource identifier.
  904. * Copy it.
  905. * Deprecated. Here for compatibility only.
  906. */
  907. $id = $result;
  908. $got_string = false;
  909. }
  910. if (!is_resource($id)) {
  911. return $this->ibaseRaiseError(DB_ERROR_NEED_MORE_DATA);
  912. }
  913. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
  914. $case_func = 'strtolower';
  915. } else {
  916. $case_func = 'strval';
  917. }
  918. $count = @ibase_num_fields($id);
  919. $res = array();
  920. if ($mode) {
  921. $res['num_fields'] = $count;
  922. }
  923. for ($i = 0; $i < $count; $i++) {
  924. $info = @ibase_field_info($id, $i);
  925. $res[$i] = array(
  926. 'table' => $got_string ? $case_func($result) : '',
  927. 'name' => $case_func($info['name']),
  928. 'type' => $info['type'],
  929. 'len' => $info['length'],
  930. 'flags' => ($got_string)
  931. ? $this->_ibaseFieldFlags($info['name'], $result)
  932. : '',
  933. );
  934. if ($mode & DB_TABLEINFO_ORDER) {
  935. $res['order'][$res[$i]['name']] = $i;
  936. }
  937. if ($mode & DB_TABLEINFO_ORDERTABLE) {
  938. $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  939. }
  940. }
  941. // free the result only if we were called on a table
  942. if ($got_string) {
  943. @ibase_free_result($id);
  944. }
  945. return $res;
  946. }
  947. // }}}
  948. // {{{ getSpecialQuery()
  949. /**
  950. * Obtains the query string needed for listing a given type of objects
  951. *
  952. * @param string $type the kind of objects you want to retrieve
  953. *
  954. * @return string the SQL query string or null if the driver doesn't
  955. * support the object type requested
  956. *
  957. * @access protected
  958. * @see DB_common::getListOf()
  959. */
  960. function getSpecialQuery($type)
  961. {
  962. switch ($type) {
  963. case 'tables':
  964. return 'SELECT DISTINCT R.RDB$RELATION_NAME FROM '
  965. . 'RDB$RELATION_FIELDS R WHERE R.RDB$SYSTEM_FLAG=0';
  966. case 'views':
  967. return 'SELECT DISTINCT RDB$VIEW_NAME from RDB$VIEW_RELATIONS';
  968. case 'users':
  969. return 'SELECT DISTINCT RDB$USER FROM RDB$USER_PRIVILEGES';
  970. default:
  971. return null;
  972. }
  973. }
  974. // }}}
  975. }
  976. /*
  977. * Local variables:
  978. * tab-width: 4
  979. * c-basic-offset: 4
  980. * End:
  981. */
  982. ?>