ibase.php 33 KB

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