ifx.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  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: Tomas V.V.Cox <cox@idecnet.com> |
  17. // | Maintainer: Daniel Convissor <danielc@php.net> |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // $Id: ifx.php,v 1.45 2004/02/19 15:34:01 danielc Exp $
  21. // Legend:
  22. // For more info on Informix errors see:
  23. // http://www.informix.com/answers/english/ierrors.htm
  24. //
  25. // TODO:
  26. // - set needed env Informix vars on connect
  27. // - implement native prepare/execute
  28. require_once 'DB/common.php';
  29. /**
  30. * Database independent query interface definition for PHP's Informix
  31. * extension.
  32. *
  33. * @package DB
  34. * @version $Id: ifx.php,v 1.45 2004/02/19 15:34:01 danielc Exp $
  35. * @category Database
  36. * @author Tomas V.V.Cox <cox@idecnet.com>
  37. */
  38. class DB_ifx extends DB_common
  39. {
  40. // {{{ properties
  41. var $connection;
  42. var $affected = 0;
  43. var $dsn = array();
  44. var $transaction_opcount = 0;
  45. var $autocommit = true;
  46. var $fetchmode = DB_FETCHMODE_ORDERED; /* Default fetch mode */
  47. // }}}
  48. // {{{ constructor
  49. function DB_ifx()
  50. {
  51. $this->phptype = 'ifx';
  52. $this->dbsyntax = 'ifx';
  53. $this->features = array(
  54. 'prepare' => false,
  55. 'pconnect' => true,
  56. 'transactions' => true,
  57. 'limit' => 'emulate'
  58. );
  59. $this->errorcode_map = array(
  60. '-201' => DB_ERROR_SYNTAX,
  61. '-206' => DB_ERROR_NOSUCHTABLE,
  62. '-217' => DB_ERROR_NOSUCHFIELD,
  63. '-239' => DB_ERROR_CONSTRAINT,
  64. '-253' => DB_ERROR_SYNTAX,
  65. '-292' => DB_ERROR_CONSTRAINT_NOT_NULL,
  66. '-310' => DB_ERROR_ALREADY_EXISTS,
  67. '-329' => DB_ERROR_NODBSELECTED,
  68. '-346' => DB_ERROR_CONSTRAINT,
  69. '-386' => DB_ERROR_CONSTRAINT_NOT_NULL,
  70. '-391' => DB_ERROR_CONSTRAINT_NOT_NULL,
  71. '-554' => DB_ERROR_SYNTAX,
  72. '-691' => DB_ERROR_CONSTRAINT,
  73. '-703' => DB_ERROR_CONSTRAINT_NOT_NULL,
  74. '-1204' => DB_ERROR_INVALID_DATE,
  75. '-1205' => DB_ERROR_INVALID_DATE,
  76. '-1206' => DB_ERROR_INVALID_DATE,
  77. '-1209' => DB_ERROR_INVALID_DATE,
  78. '-1210' => DB_ERROR_INVALID_DATE,
  79. '-1212' => DB_ERROR_INVALID_DATE,
  80. '-1213' => DB_ERROR_INVALID_NUMBER,
  81. );
  82. }
  83. // }}}
  84. // {{{ connect()
  85. /**
  86. * Connect to a database and log in as the specified user.
  87. *
  88. * @param $dsn the data source name (see DB::parseDSN for syntax)
  89. * @param $persistent (optional) whether the connection should
  90. * be persistent
  91. *
  92. * @return int DB_OK on success, a DB error code on failure
  93. */
  94. function connect($dsninfo, $persistent = false)
  95. {
  96. if (!DB::assertExtension('informix') &&
  97. !DB::assertExtension('Informix'))
  98. {
  99. return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
  100. }
  101. $this->dsn = $dsninfo;
  102. $dbhost = $dsninfo['hostspec'] ? '@' . $dsninfo['hostspec'] : '';
  103. $dbname = $dsninfo['database'] ? $dsninfo['database'] . $dbhost : '';
  104. $user = $dsninfo['username'] ? $dsninfo['username'] : '';
  105. $pw = $dsninfo['password'] ? $dsninfo['password'] : '';
  106. $connect_function = $persistent ? 'ifx_pconnect' : 'ifx_connect';
  107. $this->connection = @$connect_function($dbname, $user, $pw);
  108. if (!is_resource($this->connection)) {
  109. return $this->ifxraiseError(DB_ERROR_CONNECT_FAILED);
  110. }
  111. return DB_OK;
  112. }
  113. // }}}
  114. // {{{ disconnect()
  115. /**
  116. * Log out and disconnect from the database.
  117. *
  118. * @return bool TRUE on success, FALSE if not connected.
  119. */
  120. function disconnect()
  121. {
  122. $ret = @ifx_close($this->connection);
  123. $this->connection = null;
  124. return $ret;
  125. }
  126. // }}}
  127. // {{{ simpleQuery()
  128. /**
  129. * Send a query to Informix and return the results as a
  130. * Informix resource identifier.
  131. *
  132. * @param $query the SQL query
  133. *
  134. * @return int returns a valid Informix result for successful SELECT
  135. * queries, DB_OK for other successful queries. A DB error code
  136. * is returned on failure.
  137. */
  138. function simpleQuery($query)
  139. {
  140. $ismanip = DB::isManip($query);
  141. $this->last_query = $query;
  142. $this->affected = null;
  143. if (preg_match('/(SELECT)/i', $query)) { //TESTME: Use !DB::isManip()?
  144. // the scroll is needed for fetching absolute row numbers
  145. // in a select query result
  146. $result = @ifx_query($query, $this->connection, IFX_SCROLL);
  147. } else {
  148. if (!$this->autocommit && $ismanip) {
  149. if ($this->transaction_opcount == 0) {
  150. $result = @ifx_query('BEGIN WORK', $this->connection);
  151. if (!$result) {
  152. return $this->ifxraiseError();
  153. }
  154. }
  155. $this->transaction_opcount++;
  156. }
  157. $result = @ifx_query($query, $this->connection);
  158. }
  159. if (!$result) {
  160. return $this->ifxraiseError();
  161. }
  162. $this->affected = ifx_affected_rows($result);
  163. // Determine which queries should return data, and which
  164. // should return an error code only.
  165. if (preg_match('/(SELECT)/i', $query)) {
  166. return $result;
  167. }
  168. // XXX Testme: free results inside a transaction
  169. // may cause to stop it and commit the work?
  170. // Result has to be freed even with a insert or update
  171. ifx_free_result($result);
  172. return DB_OK;
  173. }
  174. // }}}
  175. // {{{ nextResult()
  176. /**
  177. * Move the internal ifx result pointer to the next available result
  178. *
  179. * @param a valid fbsql result resource
  180. *
  181. * @access public
  182. *
  183. * @return true if a result is available otherwise return false
  184. */
  185. function nextResult($result)
  186. {
  187. return false;
  188. }
  189. // }}}
  190. // {{{ affectedRows()
  191. /**
  192. * Gets the number of rows affected by the last query.
  193. * if the last query was a select, returns 0.
  194. *
  195. * @return number of rows affected by the last query
  196. */
  197. function affectedRows()
  198. {
  199. if (DB::isManip($this->last_query)) {
  200. return $this->affected;
  201. } else {
  202. return 0;
  203. }
  204. }
  205. // }}}
  206. // {{{ fetchInto()
  207. /**
  208. * Fetch a row and insert the data into an existing array.
  209. *
  210. * Formating of the array and the data therein are configurable.
  211. * See DB_result::fetchInto() for more information.
  212. *
  213. * @param resource $result query result identifier
  214. * @param array $arr (reference) array where data from the row
  215. * should be placed
  216. * @param int $fetchmode how the resulting array should be indexed
  217. * @param int $rownum the row number to fetch
  218. *
  219. * @return mixed DB_OK on success, NULL when end of result set is
  220. * reached or on failure
  221. *
  222. * @see DB_result::fetchInto()
  223. * @access private
  224. */
  225. function fetchInto($result, &$arr, $fetchmode, $rownum=null)
  226. {
  227. if (($rownum !== null) && ($rownum < 0)) {
  228. return null;
  229. }
  230. if ($rownum === null) {
  231. /*
  232. * Even though fetch_row() should return the next row if
  233. * $rownum is null, it doesn't in all cases. Bug 598.
  234. */
  235. $rownum = 'NEXT';
  236. } else {
  237. // Index starts at row 1, unlike most DBMS's starting at 0.
  238. $rownum++;
  239. }
  240. if (!$arr = @ifx_fetch_row($result, $rownum)) {
  241. return null;
  242. }
  243. if ($fetchmode !== DB_FETCHMODE_ASSOC) {
  244. $i=0;
  245. $order = array();
  246. foreach ($arr as $val) {
  247. $order[$i++] = $val;
  248. }
  249. $arr = $order;
  250. } elseif ($fetchmode == DB_FETCHMODE_ASSOC &&
  251. $this->options['portability'] & DB_PORTABILITY_LOWERCASE)
  252. {
  253. $arr = array_change_key_case($arr, CASE_LOWER);
  254. }
  255. if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
  256. $this->_rtrimArrayValues($arr);
  257. }
  258. if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
  259. $this->_convertNullArrayValuesToEmpty($arr);
  260. }
  261. return DB_OK;
  262. }
  263. // }}}
  264. // {{{ numRows()
  265. function numRows($result)
  266. {
  267. return $this->raiseError(DB_ERROR_NOT_CAPABLE);
  268. }
  269. // }}}
  270. // {{{ numCols()
  271. /**
  272. * Get the number of columns in a result set.
  273. *
  274. * @param $result Informix result identifier
  275. *
  276. * @return int the number of columns per row in $result
  277. */
  278. function numCols($result)
  279. {
  280. if (!$cols = @ifx_num_fields($result)) {
  281. return $this->ifxraiseError();
  282. }
  283. return $cols;
  284. }
  285. // }}}
  286. // {{{ freeResult()
  287. /**
  288. * Free the internal resources associated with $result.
  289. *
  290. * @param $result Informix result identifier
  291. *
  292. * @return bool TRUE on success, FALSE if $result is invalid
  293. */
  294. function freeResult($result)
  295. {
  296. return @ifx_free_result($result);
  297. }
  298. // }}}
  299. // {{{ autoCommit()
  300. /**
  301. * Enable/disable automatic commits
  302. */
  303. function autoCommit($onoff = true)
  304. {
  305. // XXX if $this->transaction_opcount > 0, we should probably
  306. // issue a warning here.
  307. $this->autocommit = $onoff ? true : false;
  308. return DB_OK;
  309. }
  310. // }}}
  311. // {{{ commit()
  312. /**
  313. * Commit the current transaction.
  314. */
  315. function commit()
  316. {
  317. if ($this->transaction_opcount > 0) {
  318. $result = @ifx_query('COMMIT WORK', $this->connection);
  319. $this->transaction_opcount = 0;
  320. if (!$result) {
  321. return $this->ifxRaiseError();
  322. }
  323. }
  324. return DB_OK;
  325. }
  326. // }}}
  327. // {{{ rollback()
  328. /**
  329. * Roll back (undo) the current transaction.
  330. */
  331. function rollback()
  332. {
  333. if ($this->transaction_opcount > 0) {
  334. $result = @ifx_query('ROLLBACK WORK', $this->connection);
  335. $this->transaction_opcount = 0;
  336. if (!$result) {
  337. return $this->ifxRaiseError();
  338. }
  339. }
  340. return DB_OK;
  341. }
  342. // }}}
  343. // {{{ ifxraiseError()
  344. /**
  345. * Gather information about an error, then use that info to create a
  346. * DB error object and finally return that object.
  347. *
  348. * @param integer $errno PEAR error number (usually a DB constant) if
  349. * manually raising an error
  350. * @return object DB error object
  351. * @see errorNative()
  352. * @see errorCode()
  353. * @see DB_common::raiseError()
  354. */
  355. function ifxraiseError($errno = null)
  356. {
  357. if ($errno === null) {
  358. $errno = $this->errorCode(ifx_error());
  359. }
  360. return $this->raiseError($errno, null, null, null,
  361. $this->errorNative());
  362. }
  363. // }}}
  364. // {{{ errorCode()
  365. /**
  366. * Map native error codes to DB's portable ones.
  367. *
  368. * Requires that the DB implementation's constructor fills
  369. * in the <var>$errorcode_map</var> property.
  370. *
  371. * @param string $nativecode error code returned by the database
  372. * @return int a portable DB error code, or DB_ERROR if this DB
  373. * implementation has no mapping for the given error code.
  374. */
  375. function errorCode($nativecode)
  376. {
  377. if (ereg('SQLCODE=(.*)]', $nativecode, $match)) {
  378. $code = $match[1];
  379. if (isset($this->errorcode_map[$code])) {
  380. return $this->errorcode_map[$code];
  381. }
  382. }
  383. return DB_ERROR;
  384. }
  385. // }}}
  386. // {{{ errorNative()
  387. /**
  388. * Get the native error message of the last error (if any) that
  389. * occured on the current connection.
  390. *
  391. * @return int native Informix error code
  392. */
  393. function errorNative()
  394. {
  395. return ifx_error() . ' ' . ifx_errormsg();
  396. }
  397. // }}}
  398. // {{{ getSpecialQuery()
  399. /**
  400. * Returns the query needed to get some backend info
  401. * @param string $type What kind of info you want to retrieve
  402. * @return string The SQL query string
  403. */
  404. function getSpecialQuery($type)
  405. {
  406. switch ($type) {
  407. case 'tables':
  408. return 'select tabname from systables where tabid >= 100';
  409. default:
  410. return null;
  411. }
  412. }
  413. // }}}
  414. // {{{ tableInfo()
  415. /**
  416. * Returns information about a table or a result set.
  417. *
  418. * NOTE: only supports 'table' if <var>$result</var> is a table name.
  419. *
  420. * If analyzing a query result and the result has duplicate field names,
  421. * an error will be raised saying
  422. * <samp>can't distinguish duplicate field names</samp>.
  423. *
  424. * @param object|string $result DB_result object from a query or a
  425. * string containing the name of a table
  426. * @param int $mode a valid tableInfo mode
  427. * @return array an associative array with the information requested
  428. * or an error object if something is wrong
  429. * @access public
  430. * @internal
  431. * @since 1.6.0
  432. * @see DB_common::tableInfo()
  433. */
  434. function tableInfo($result, $mode = null)
  435. {
  436. if (isset($result->result)) {
  437. /*
  438. * Probably received a result object.
  439. * Extract the result resource identifier.
  440. */
  441. $id = $result->result;
  442. $got_string = false;
  443. } elseif (is_string($result)) {
  444. /*
  445. * Probably received a table name.
  446. * Create a result resource identifier.
  447. */
  448. $id = @ifx_query("SELECT * FROM $result WHERE 1=0",
  449. $this->connection);
  450. $got_string = true;
  451. } else {
  452. /*
  453. * Probably received a result resource identifier.
  454. * Copy it.
  455. */
  456. $id = $result;
  457. $got_string = false;
  458. }
  459. if (!is_resource($id)) {
  460. return $this->ifxRaiseError(DB_ERROR_NEED_MORE_DATA);
  461. }
  462. $flds = @ifx_fieldproperties($id);
  463. $count = @ifx_num_fields($id);
  464. if (count($flds) != $count) {
  465. return $this->raiseError("can't distinguish duplicate field names");
  466. }
  467. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
  468. $case_func = 'strtolower';
  469. } else {
  470. $case_func = 'strval';
  471. }
  472. $i = 0;
  473. // made this IF due to performance (one if is faster than $count if's)
  474. if (!$mode) {
  475. foreach ($flds as $key => $value) {
  476. $props = explode(';', $value);
  477. $res[$i]['table'] = $got_string ? $case_func($result) : '';
  478. $res[$i]['name'] = $case_func($key);
  479. $res[$i]['type'] = $props[0];
  480. $res[$i]['len'] = $props[1];
  481. $res[$i]['flags'] = $props[4] == 'N' ? 'not_null' : '';
  482. $i++;
  483. }
  484. } else { // full
  485. $res['num_fields'] = $count;
  486. foreach ($flds as $key => $value) {
  487. $props = explode(';', $value);
  488. $res[$i]['table'] = $got_string ? $case_func($result) : '';
  489. $res[$i]['name'] = $case_func($key);
  490. $res[$i]['type'] = $props[0];
  491. $res[$i]['len'] = $props[1];
  492. $res[$i]['flags'] = $props[4] == 'N' ? 'not_null' : '';
  493. if ($mode & DB_TABLEINFO_ORDER) {
  494. $res['order'][$res[$i]['name']] = $i;
  495. }
  496. if ($mode & DB_TABLEINFO_ORDERTABLE) {
  497. $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  498. }
  499. $i++;
  500. }
  501. }
  502. // free the result only if we were called on a table
  503. if ($got_string) {
  504. @ifx_free_result($id);
  505. }
  506. return $res;
  507. }
  508. // }}}
  509. }
  510. /*
  511. * Local variables:
  512. * tab-width: 4
  513. * c-basic-offset: 4
  514. * End:
  515. */
  516. ?>