ibase.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2004 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license, |
  9. // | that is bundled with this package in the file LICENSE, and is |
  10. // | available at through the world-wide-web at |
  11. // | http://www.php.net/license/2_02.txt. |
  12. // | If you did not receive a copy of the PHP license and are unable to |
  13. // | obtain it through the world-wide-web, please send a note to |
  14. // | license@php.net so we can mail you a copy immediately. |
  15. // +----------------------------------------------------------------------+
  16. // | Author: Sterling Hughes <sterling@php.net> |
  17. // | Maintainer: Daniel Convissor <danielc@php.net> |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // $Id: ibase.php,v 1.63 2004/02/19 19:24:14 danielc Exp $
  21. // Bugs:
  22. // - If dbsyntax is not firebird, the limitQuery may fail
  23. require_once 'DB/common.php';
  24. /**
  25. * Database independent query interface definition for PHP's Interbase
  26. * extension.
  27. *
  28. * @package DB
  29. * @version $Id: ibase.php,v 1.63 2004/02/19 19:24:14 danielc Exp $
  30. * @category Database
  31. * @author Sterling Hughes <sterling@php.net>
  32. */
  33. class DB_ibase extends DB_common
  34. {
  35. // {{{ properties
  36. var $connection;
  37. var $phptype, $dbsyntax;
  38. var $autocommit = 1;
  39. var $manip_query = array();
  40. // }}}
  41. // {{{ constructor
  42. function DB_ibase()
  43. {
  44. $this->DB_common();
  45. $this->phptype = 'ibase';
  46. $this->dbsyntax = 'ibase';
  47. $this->features = array(
  48. 'prepare' => true,
  49. 'pconnect' => true,
  50. 'transactions' => true,
  51. 'limit' => false
  52. );
  53. // just a few of the tons of Interbase error codes listed in the
  54. // Language Reference section of the Interbase manual
  55. $this->errorcode_map = array(
  56. -104 => DB_ERROR_SYNTAX,
  57. -150 => DB_ERROR_ACCESS_VIOLATION,
  58. -151 => DB_ERROR_ACCESS_VIOLATION,
  59. -155 => DB_ERROR_NOSUCHTABLE,
  60. 88 => DB_ERROR_NOSUCHTABLE,
  61. -157 => DB_ERROR_NOSUCHFIELD,
  62. -158 => DB_ERROR_VALUE_COUNT_ON_ROW,
  63. -170 => DB_ERROR_MISMATCH,
  64. -171 => DB_ERROR_MISMATCH,
  65. -172 => DB_ERROR_INVALID,
  66. -204 => DB_ERROR_INVALID,
  67. -205 => DB_ERROR_NOSUCHFIELD,
  68. -206 => DB_ERROR_NOSUCHFIELD,
  69. -208 => DB_ERROR_INVALID,
  70. -219 => DB_ERROR_NOSUCHTABLE,
  71. -297 => DB_ERROR_CONSTRAINT,
  72. -530 => DB_ERROR_CONSTRAINT,
  73. -607 => DB_ERROR_NOSUCHTABLE,
  74. -803 => DB_ERROR_CONSTRAINT,
  75. -551 => DB_ERROR_ACCESS_VIOLATION,
  76. -552 => DB_ERROR_ACCESS_VIOLATION,
  77. -922 => DB_ERROR_NOSUCHDB,
  78. -923 => DB_ERROR_CONNECT_FAILED,
  79. -924 => DB_ERROR_CONNECT_FAILED
  80. );
  81. }
  82. // }}}
  83. // {{{ connect()
  84. function connect($dsninfo, $persistent = false)
  85. {
  86. if (!DB::assertExtension('interbase')) {
  87. return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
  88. }
  89. $this->dsn = $dsninfo;
  90. $dbhost = $dsninfo['hostspec'] ?
  91. ($dsninfo['hostspec'] . ':' . $dsninfo['database']) :
  92. $dsninfo['database'];
  93. $connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect';
  94. $params = array();
  95. $params[] = $dbhost;
  96. $params[] = $dsninfo['username'] ? $dsninfo['username'] : null;
  97. $params[] = $dsninfo['password'] ? $dsninfo['password'] : null;
  98. $params[] = isset($dsninfo['charset']) ? $dsninfo['charset'] : null;
  99. $params[] = isset($dsninfo['buffers']) ? $dsninfo['buffers'] : null;
  100. $params[] = isset($dsninfo['dialect']) ? $dsninfo['dialect'] : null;
  101. $params[] = isset($dsninfo['role']) ? $dsninfo['role'] : null;
  102. $conn = @call_user_func_array($connect_function, $params);
  103. if (!$conn) {
  104. return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED);
  105. }
  106. $this->connection = $conn;
  107. if ($this->dsn['dbsyntax'] == 'firebird') {
  108. $this->features['limit'] = 'alter';
  109. }
  110. return DB_OK;
  111. }
  112. // }}}
  113. // {{{ disconnect()
  114. function disconnect()
  115. {
  116. $ret = @ibase_close($this->connection);
  117. $this->connection = null;
  118. return $ret;
  119. }
  120. // }}}
  121. // {{{ simpleQuery()
  122. function simpleQuery($query)
  123. {
  124. $ismanip = DB::isManip($query);
  125. $this->last_query = $query;
  126. $query = $this->modifyQuery($query);
  127. $result = @ibase_query($this->connection, $query);
  128. if (!$result) {
  129. return $this->ibaseRaiseError();
  130. }
  131. if ($this->autocommit && $ismanip) {
  132. ibase_commit($this->connection);
  133. }
  134. // Determine which queries that should return data, and which
  135. // should return an error code only.
  136. return $ismanip ? DB_OK : $result;
  137. }
  138. // }}}
  139. // {{{ modifyLimitQuery()
  140. /**
  141. * This method is used by backends to alter limited queries
  142. * Uses the new FIRST n SKIP n Firebird 1.0 syntax, so it is
  143. * only compatible with Firebird 1.x
  144. *
  145. * @param string $query query to modify
  146. * @param integer $from the row to start to fetching
  147. * @param integer $count the numbers of rows to fetch
  148. *
  149. * @return the new (modified) query
  150. * @author Ludovico Magnocavallo <ludo@sumatrasolutions.com>
  151. * @access private
  152. */
  153. function modifyLimitQuery($query, $from, $count)
  154. {
  155. if ($this->dsn['dbsyntax'] == 'firebird') {
  156. //$from++; // SKIP starts from 1, ie SKIP 1 starts from the first record
  157. // (cox) Seems that SKIP starts in 0
  158. $query = preg_replace('/^\s*select\s(.*)$/is',
  159. "SELECT FIRST $count SKIP $from $1", $query);
  160. }
  161. return $query;
  162. }
  163. // }}}
  164. // {{{ nextResult()
  165. /**
  166. * Move the internal ibase result pointer to the next available result
  167. *
  168. * @param a valid fbsql result resource
  169. *
  170. * @access public
  171. *
  172. * @return true if a result is available otherwise return false
  173. */
  174. function nextResult($result)
  175. {
  176. return false;
  177. }
  178. // }}}
  179. // {{{ fetchInto()
  180. /**
  181. * Fetch a row and insert the data into an existing array.
  182. *
  183. * Formating of the array and the data therein are configurable.
  184. * See DB_result::fetchInto() for more information.
  185. *
  186. * @param resource $result query result identifier
  187. * @param array $arr (reference) array where data from the row
  188. * should be placed
  189. * @param int $fetchmode how the resulting array should be indexed
  190. * @param int $rownum the row number to fetch
  191. *
  192. * @return mixed DB_OK on success, NULL when end of result set is
  193. * reached or on failure
  194. *
  195. * @see DB_result::fetchInto()
  196. * @access private
  197. */
  198. function fetchInto($result, &$arr, $fetchmode, $rownum=null)
  199. {
  200. if ($rownum !== NULL) {
  201. return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
  202. }
  203. if ($fetchmode & DB_FETCHMODE_ASSOC) {
  204. if (function_exists('ibase_fetch_assoc')) {
  205. $arr = @ibase_fetch_assoc($result);
  206. } else {
  207. $arr = get_object_vars(ibase_fetch_object($result));
  208. }
  209. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
  210. $arr = array_change_key_case($arr, CASE_LOWER);
  211. }
  212. } else {
  213. $arr = @ibase_fetch_row($result);
  214. }
  215. if (!$arr) {
  216. if ($errmsg = ibase_errmsg()) {
  217. return $this->ibaseRaiseError(null, $errmsg);
  218. } else {
  219. return null;
  220. }
  221. }
  222. if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
  223. $this->_rtrimArrayValues($arr);
  224. }
  225. if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
  226. $this->_convertNullArrayValuesToEmpty($arr);
  227. }
  228. return DB_OK;
  229. }
  230. // }}}
  231. // {{{ freeResult()
  232. function freeResult($result)
  233. {
  234. return @ibase_free_result($result);
  235. }
  236. // }}}
  237. // {{{ freeQuery()
  238. function freeQuery($query)
  239. {
  240. ibase_free_query($query);
  241. return true;
  242. }
  243. // }}}
  244. // {{{ numCols()
  245. function numCols($result)
  246. {
  247. $cols = ibase_num_fields($result);
  248. if (!$cols) {
  249. return $this->ibaseRaiseError();
  250. }
  251. return $cols;
  252. }
  253. // }}}
  254. // {{{ prepare()
  255. /**
  256. * Prepares a query for multiple execution with execute().
  257. *
  258. * prepare() requires a generic query as string like <code>
  259. * INSERT INTO numbers VALUES (?, ?, ?)
  260. * </code>. The <kbd>?</kbd> characters are placeholders.
  261. *
  262. * Three types of placeholders can be used:
  263. * + <kbd>?</kbd> a quoted scalar value, i.e. strings, integers
  264. * + <kbd>!</kbd> value is inserted 'as is'
  265. * + <kbd>&</kbd> requires a file name. The file's contents get
  266. * inserted into the query (i.e. saving binary
  267. * data in a db)
  268. *
  269. * Use backslashes to escape placeholder characters if you don't want
  270. * them to be interpreted as placeholders. Example: <code>
  271. * "UPDATE foo SET col=? WHERE col='over \& under'"
  272. * </code>
  273. *
  274. * @param string $query query to be prepared
  275. * @return mixed DB statement resource on success. DB_Error on failure.
  276. */
  277. function prepare($query)
  278. {
  279. $tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1,
  280. PREG_SPLIT_DELIM_CAPTURE);
  281. $token = 0;
  282. $types = array();
  283. $newquery = '';
  284. foreach ($tokens as $key => $val) {
  285. switch ($val) {
  286. case '?':
  287. $types[$token++] = DB_PARAM_SCALAR;
  288. break;
  289. case '&':
  290. $types[$token++] = DB_PARAM_OPAQUE;
  291. break;
  292. case '!':
  293. $types[$token++] = DB_PARAM_MISC;
  294. break;
  295. default:
  296. $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val);
  297. $newquery .= $tokens[$key] . '?';
  298. }
  299. }
  300. $newquery = substr($newquery, 0, -1);
  301. $this->last_query = $query;
  302. $newquery = $this->modifyQuery($newquery);
  303. $stmt = ibase_prepare($this->connection, $newquery);
  304. $this->prepare_types[(int)$stmt] = $types;
  305. $this->manip_query[(int)$stmt] = DB::isManip($query);
  306. return $stmt;
  307. }
  308. // }}}
  309. // {{{ execute()
  310. /**
  311. * Executes a DB statement prepared with prepare().
  312. *
  313. * @param resource $stmt a DB statement resource returned from prepare()
  314. * @param mixed $data array, string or numeric data to be used in
  315. * execution of the statement. Quantity of items
  316. * passed must match quantity of placeholders in
  317. * query: meaning 1 for non-array items or the
  318. * quantity of elements in the array.
  319. * @return object a new DB_Result or a DB_Error when fail
  320. * @see DB_ibase::prepare()
  321. * @access public
  322. */
  323. function &execute($stmt, $data = array())
  324. {
  325. if (!is_array($data)) {
  326. $data = array($data);
  327. }
  328. $types =& $this->prepare_types[$stmt];
  329. if (count($types) != count($data)) {
  330. $tmp =& $this->raiseError(DB_ERROR_MISMATCH);
  331. return $tmp;
  332. }
  333. $i = 0;
  334. foreach ($data as $key => $value) {
  335. if ($types[$i] == DB_PARAM_MISC) {
  336. /*
  337. * ibase doesn't seem to have the ability to pass a
  338. * parameter along unchanged, so strip off quotes from start
  339. * and end, plus turn two single quotes to one single quote,
  340. * in order to avoid the quotes getting escaped by
  341. * ibase and ending up in the database.
  342. */
  343. $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]);
  344. $data[$key] = str_replace("''", "'", $data[$key]);
  345. } elseif ($types[$i] == DB_PARAM_OPAQUE) {
  346. $fp = @fopen($data[$key], 'rb');
  347. if (!$fp) {
  348. $tmp =& $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
  349. return $tmp;
  350. }
  351. $data[$key] = fread($fp, filesize($data[$key]));
  352. fclose($fp);
  353. }
  354. $i++;
  355. }
  356. array_unshift($data, $stmt);
  357. $res = call_user_func_array('ibase_execute', $data);
  358. if (!$res) {
  359. $tmp =& $this->ibaseRaiseError();
  360. return $tmp;
  361. }
  362. /* XXX need this?
  363. if ($this->autocommit && $this->manip_query[(int)$stmt]) {
  364. ibase_commit($this->connection);
  365. }*/
  366. if ($this->manip_query[(int)$stmt]) {
  367. $tmp = DB_OK;
  368. } else {
  369. $tmp =& new DB_result($this, $res);
  370. }
  371. return $tmp;
  372. }
  373. /**
  374. * Free the internal resources associated with a prepared query.
  375. *
  376. * @param $stmt The interbase_query resource type
  377. *
  378. * @return bool TRUE on success, FALSE if $result is invalid
  379. */
  380. function freePrepared($stmt)
  381. {
  382. if (!is_resource($stmt)) {
  383. return false;
  384. }
  385. ibase_free_query($stmt);
  386. unset($this->prepare_tokens[(int)$stmt]);
  387. unset($this->prepare_types[(int)$stmt]);
  388. unset($this->manip_query[(int)$stmt]);
  389. return true;
  390. }
  391. // }}}
  392. // {{{ autoCommit()
  393. function autoCommit($onoff = false)
  394. {
  395. $this->autocommit = $onoff ? 1 : 0;
  396. return DB_OK;
  397. }
  398. // }}}
  399. // {{{ commit()
  400. function commit()
  401. {
  402. return ibase_commit($this->connection);
  403. }
  404. // }}}
  405. // {{{ rollback()
  406. function rollback()
  407. {
  408. return ibase_rollback($this->connection);
  409. }
  410. // }}}
  411. // {{{ transactionInit()
  412. function transactionInit($trans_args = 0)
  413. {
  414. return $trans_args ? ibase_trans($trans_args, $this->connection) : ibase_trans();
  415. }
  416. // }}}
  417. // {{{ nextId()
  418. /**
  419. * Returns the next free id in a sequence
  420. *
  421. * @param string $seq_name name of the sequence
  422. * @param boolean $ondemand when true, the seqence is automatically
  423. * created if it does not exist
  424. *
  425. * @return int the next id number in the sequence. DB_Error if problem.
  426. *
  427. * @internal
  428. * @see DB_common::nextID()
  429. * @access public
  430. */
  431. function nextId($seq_name, $ondemand = true)
  432. {
  433. $sqn = strtoupper($this->getSequenceName($seq_name));
  434. $repeat = 0;
  435. do {
  436. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  437. $result =& $this->query("SELECT GEN_ID(${sqn}, 1) "
  438. . 'FROM RDB$GENERATORS '
  439. . "WHERE RDB\$GENERATOR_NAME='${sqn}'");
  440. $this->popErrorHandling();
  441. if ($ondemand && DB::isError($result)) {
  442. $repeat = 1;
  443. $result = $this->createSequence($seq_name);
  444. if (DB::isError($result)) {
  445. return $result;
  446. }
  447. } else {
  448. $repeat = 0;
  449. }
  450. } while ($repeat);
  451. if (DB::isError($result)) {
  452. return $this->raiseError($result);
  453. }
  454. $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
  455. $result->free();
  456. return $arr[0];
  457. }
  458. // }}}
  459. // {{{ createSequence()
  460. /**
  461. * Create the sequence
  462. *
  463. * @param string $seq_name the name of the sequence
  464. * @return mixed DB_OK on success or DB error on error
  465. * @access public
  466. */
  467. function createSequence($seq_name)
  468. {
  469. $sqn = strtoupper($this->getSequenceName($seq_name));
  470. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  471. $result = $this->query("CREATE GENERATOR ${sqn}");
  472. $this->popErrorHandling();
  473. return $result;
  474. }
  475. // }}}
  476. // {{{ dropSequence()
  477. /**
  478. * Drop a sequence
  479. *
  480. * @param string $seq_name the name of the sequence
  481. * @return mixed DB_OK on success or DB error on error
  482. * @access public
  483. */
  484. function dropSequence($seq_name)
  485. {
  486. $sqn = strtoupper($this->getSequenceName($seq_name));
  487. return $this->query('DELETE FROM RDB$GENERATORS '
  488. . "WHERE RDB\$GENERATOR_NAME='${sqn}'");
  489. }
  490. // }}}
  491. // {{{ _ibaseFieldFlags()
  492. /**
  493. * get the Flags of a Field
  494. *
  495. * @param string $field_name the name of the field
  496. * @param string $table_name the name of the table
  497. *
  498. * @return string The flags of the field ("primary_key", "unique_key", "not_null"
  499. * "default", "computed" and "blob" are supported)
  500. * @access private
  501. */
  502. function _ibaseFieldFlags($field_name, $table_name)
  503. {
  504. $sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE'
  505. .' FROM RDB$INDEX_SEGMENTS I'
  506. .' JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME'
  507. .' WHERE I.RDB$FIELD_NAME=\'' . $field_name . '\''
  508. .' AND UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\'';
  509. $result = ibase_query($this->connection, $sql);
  510. if (!$result) {
  511. return $this->ibaseRaiseError();
  512. }
  513. $flags = '';
  514. if ($obj = @ibase_fetch_object($result)) {
  515. ibase_free_result($result);
  516. if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'PRIMARY KEY') {
  517. $flags .= 'primary_key ';
  518. }
  519. if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'UNIQUE') {
  520. $flags .= 'unique_key ';
  521. }
  522. }
  523. $sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,'
  524. .' R.RDB$DEFAULT_SOURCE AS DSOURCE,'
  525. .' F.RDB$FIELD_TYPE AS FTYPE,'
  526. .' F.RDB$COMPUTED_SOURCE AS CSOURCE'
  527. .' FROM RDB$RELATION_FIELDS R '
  528. .' JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME'
  529. .' WHERE UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\''
  530. .' AND R.RDB$FIELD_NAME=\'' . $field_name . '\'';
  531. $result = ibase_query($this->connection, $sql);
  532. if (!$result) {
  533. return $this->ibaseRaiseError();
  534. }
  535. if ($obj = @ibase_fetch_object($result)) {
  536. ibase_free_result($result);
  537. if (isset($obj->NFLAG)) {
  538. $flags .= 'not_null ';
  539. }
  540. if (isset($obj->DSOURCE)) {
  541. $flags .= 'default ';
  542. }
  543. if (isset($obj->CSOURCE)) {
  544. $flags .= 'computed ';
  545. }
  546. if (isset($obj->FTYPE) && $obj->FTYPE == 261) {
  547. $flags .= 'blob ';
  548. }
  549. }
  550. return trim($flags);
  551. }
  552. // }}}
  553. // {{{ tableInfo()
  554. /**
  555. * Returns information about a table or a result set.
  556. *
  557. * NOTE: only supports 'table' and 'flags' if <var>$result</var>
  558. * is a table name.
  559. *
  560. * @param object|string $result DB_result object from a query or a
  561. * string containing the name of a table
  562. * @param int $mode a valid tableInfo mode
  563. * @return array an associative array with the information requested
  564. * or an error object if something is wrong
  565. * @access public
  566. * @internal
  567. * @see DB_common::tableInfo()
  568. */
  569. function tableInfo($result, $mode = null)
  570. {
  571. if (isset($result->result)) {
  572. /*
  573. * Probably received a result object.
  574. * Extract the result resource identifier.
  575. */
  576. $id = $result->result;
  577. $got_string = false;
  578. } elseif (is_string($result)) {
  579. /*
  580. * Probably received a table name.
  581. * Create a result resource identifier.
  582. */
  583. $id = @ibase_query($this->connection,
  584. "SELECT * FROM $result WHERE 1=0");
  585. $got_string = true;
  586. } else {
  587. /*
  588. * Probably received a result resource identifier.
  589. * Copy it.
  590. * Depricated. Here for compatibility only.
  591. */
  592. $id = $result;
  593. $got_string = false;
  594. }
  595. if (!is_resource($id)) {
  596. return $this->ibaseRaiseError(DB_ERROR_NEED_MORE_DATA);
  597. }
  598. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
  599. $case_func = 'strtolower';
  600. } else {
  601. $case_func = 'strval';
  602. }
  603. $count = @ibase_num_fields($id);
  604. // made this IF due to performance (one if is faster than $count if's)
  605. if (!$mode) {
  606. for ($i=0; $i<$count; $i++) {
  607. $info = @ibase_field_info($id, $i);
  608. $res[$i]['table'] = $got_string ? $case_func($result) : '';
  609. $res[$i]['name'] = $case_func($info['name']);
  610. $res[$i]['type'] = $info['type'];
  611. $res[$i]['len'] = $info['length'];
  612. $res[$i]['flags'] = ($got_string) ? $this->_ibaseFieldFlags($info['name'], $result) : '';
  613. }
  614. } else { // full
  615. $res['num_fields']= $count;
  616. for ($i=0; $i<$count; $i++) {
  617. $info = @ibase_field_info($id, $i);
  618. $res[$i]['table'] = $got_string ? $case_func($result) : '';
  619. $res[$i]['name'] = $case_func($info['name']);
  620. $res[$i]['type'] = $info['type'];
  621. $res[$i]['len'] = $info['length'];
  622. $res[$i]['flags'] = ($got_string) ? $this->_ibaseFieldFlags($info['name'], $result) : '';
  623. if ($mode & DB_TABLEINFO_ORDER) {
  624. $res['order'][$res[$i]['name']] = $i;
  625. }
  626. if ($mode & DB_TABLEINFO_ORDERTABLE) {
  627. $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  628. }
  629. }
  630. }
  631. // free the result only if we were called on a table
  632. if ($got_string) {
  633. ibase_free_result($id);
  634. }
  635. return $res;
  636. }
  637. // }}}
  638. // {{{ ibaseRaiseError()
  639. /**
  640. * Gather information about an error, then use that info to create a
  641. * DB error object and finally return that object.
  642. *
  643. * @param integer $db_errno PEAR error number (usually a DB constant) if
  644. * manually raising an error
  645. * @param string $native_errmsg text of error message if known
  646. * @return object DB error object
  647. * @see DB_common::errorCode()
  648. * @see DB_common::raiseError()
  649. */
  650. function &ibaseRaiseError($db_errno = null, $native_errmsg = null)
  651. {
  652. if ($native_errmsg === null) {
  653. $native_errmsg = ibase_errmsg();
  654. }
  655. // memo for the interbase php module hackers: we need something similar
  656. // to mysql_errno() to retrieve error codes instead of this ugly hack
  657. if (preg_match('/^([^0-9\-]+)([0-9\-]+)\s+(.*)$/', $native_errmsg, $m)) {
  658. $native_errno = (int)$m[2];
  659. } else {
  660. $native_errno = null;
  661. }
  662. // try to map the native error to the DB one
  663. if ($db_errno === null) {
  664. if ($native_errno) {
  665. // try to interpret Interbase error code (that's why we need ibase_errno()
  666. // in the interbase module to return the real error code)
  667. switch ($native_errno) {
  668. case -204:
  669. if (is_int(strpos($m[3], 'Table unknown'))) {
  670. $db_errno = DB_ERROR_NOSUCHTABLE;
  671. }
  672. break;
  673. default:
  674. $db_errno = $this->errorCode($native_errno);
  675. }
  676. } else {
  677. $error_regexps = array(
  678. '/[tT]able not found/' => DB_ERROR_NOSUCHTABLE,
  679. '/[tT]able .* already exists/' => DB_ERROR_ALREADY_EXISTS,
  680. '/validation error for column .* value "\*\*\* null/' => DB_ERROR_CONSTRAINT_NOT_NULL,
  681. '/violation of [\w ]+ constraint/' => DB_ERROR_CONSTRAINT,
  682. '/conversion error from string/' => DB_ERROR_INVALID_NUMBER,
  683. '/no permission for/' => DB_ERROR_ACCESS_VIOLATION,
  684. '/arithmetic exception, numeric overflow, or string truncation/' => DB_ERROR_DIVZERO
  685. );
  686. foreach ($error_regexps as $regexp => $code) {
  687. if (preg_match($regexp, $native_errmsg)) {
  688. $db_errno = $code;
  689. $native_errno = null;
  690. break;
  691. }
  692. }
  693. }
  694. }
  695. $tmp =& $this->raiseError($db_errno, null, null, null, $native_errmsg);
  696. return $tmp;
  697. }
  698. // }}}
  699. }
  700. /*
  701. * Local variables:
  702. * tab-width: 4
  703. * c-basic-offset: 4
  704. * End:
  705. */
  706. ?>