fbsql.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  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: Frank M. Kromann <frank@frontbase.com> |
  17. // | Maintainer: Daniel Convissor <danielc@php.net> |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // $Id: fbsql.php,v 1.31 2004/02/18 05:37:31 danielc Exp $
  21. // XXX legend:
  22. //
  23. // XXX ERRORMSG: The error message from the fbsql function should
  24. // be registered here.
  25. //
  26. // TODO/wishlist:
  27. // longReadlen
  28. // binmode
  29. require_once 'DB/common.php';
  30. /**
  31. * Database independent query interface definition for PHP's FrontBase
  32. * extension.
  33. *
  34. * @package DB
  35. * @version $Id: fbsql.php,v 1.31 2004/02/18 05:37:31 danielc Exp $
  36. * @category Database
  37. * @author Frank M. Kromann <frank@frontbase.com>
  38. */
  39. class DB_fbsql extends DB_common
  40. {
  41. // {{{ properties
  42. var $connection;
  43. var $phptype, $dbsyntax;
  44. var $prepare_tokens = array();
  45. var $prepare_types = array();
  46. var $num_rows = array();
  47. var $fetchmode = DB_FETCHMODE_ORDERED; /* Default fetch mode */
  48. // }}}
  49. // {{{ constructor
  50. /**
  51. * DB_fbsql constructor.
  52. *
  53. * @access public
  54. */
  55. function DB_fbsql()
  56. {
  57. $this->DB_common();
  58. $this->phptype = 'fbsql';
  59. $this->dbsyntax = 'fbsql';
  60. $this->features = array(
  61. 'prepare' => false,
  62. 'pconnect' => true,
  63. 'transactions' => true,
  64. 'limit' => 'emulate'
  65. );
  66. $this->errorcode_map = array(
  67. 1004 => DB_ERROR_CANNOT_CREATE,
  68. 1005 => DB_ERROR_CANNOT_CREATE,
  69. 1006 => DB_ERROR_CANNOT_CREATE,
  70. 1007 => DB_ERROR_ALREADY_EXISTS,
  71. 1008 => DB_ERROR_CANNOT_DROP,
  72. 1046 => DB_ERROR_NODBSELECTED,
  73. 1050 => DB_ERROR_ALREADY_EXISTS,
  74. 1051 => DB_ERROR_NOSUCHTABLE,
  75. 1054 => DB_ERROR_NOSUCHFIELD,
  76. 1062 => DB_ERROR_ALREADY_EXISTS,
  77. 1064 => DB_ERROR_SYNTAX,
  78. 1100 => DB_ERROR_NOT_LOCKED,
  79. 1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
  80. 1146 => DB_ERROR_NOSUCHTABLE,
  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. * @access public
  92. * @return int DB_OK on success, a DB error on failure
  93. */
  94. function connect($dsninfo, $persistent = false)
  95. {
  96. if (!DB::assertExtension('fbsql')) {
  97. return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
  98. }
  99. $this->dsn = $dsninfo;
  100. $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost';
  101. $php_errormsg = '';
  102. $connect_function = $persistent ? 'fbsql_pconnect' : 'fbsql_connect';
  103. if ($dbhost && $dsninfo['username'] && $dsninfo['password']) {
  104. $conn = @$connect_function($dbhost, $dsninfo['username'],
  105. $dsninfo['password']);
  106. } elseif ($dbhost && $dsninfo['username']) {
  107. $conn = @$connect_function($dbhost, $dsninfo['username']);
  108. } elseif ($dbhost) {
  109. $conn = @$connect_function($dbhost);
  110. } else {
  111. $conn = false;
  112. }
  113. if (!$conn) {
  114. if (empty($php_errormsg)) {
  115. return $this->raiseError(DB_ERROR_CONNECT_FAILED);
  116. } else {
  117. return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null,
  118. null, $php_errormsg);
  119. }
  120. }
  121. if ($dsninfo['database']) {
  122. if (!fbsql_select_db($dsninfo['database'], $conn)) {
  123. return $this->fbsqlRaiseError();
  124. }
  125. }
  126. $this->connection = $conn;
  127. return DB_OK;
  128. }
  129. // }}}
  130. // {{{ disconnect()
  131. /**
  132. * Log out and disconnect from the database.
  133. *
  134. * @access public
  135. *
  136. * @return bool TRUE on success, FALSE if not connected.
  137. */
  138. function disconnect()
  139. {
  140. $ret = fbsql_close($this->connection);
  141. $this->connection = null;
  142. return $ret;
  143. }
  144. // }}}
  145. // {{{ simpleQuery()
  146. /**
  147. * Send a query to fbsql and return the results as a fbsql resource
  148. * identifier.
  149. *
  150. * @param the SQL query
  151. *
  152. * @access public
  153. *
  154. * @return mixed returns a valid fbsql result for successful SELECT
  155. * queries, DB_OK for other successful queries. A DB error is
  156. * returned on failure.
  157. */
  158. function simpleQuery($query)
  159. {
  160. $this->last_query = $query;
  161. $query = $this->modifyQuery($query);
  162. $result = @fbsql_query("$query;", $this->connection);
  163. if (!$result) {
  164. return $this->fbsqlRaiseError();
  165. }
  166. // Determine which queries that should return data, and which
  167. // should return an error code only.
  168. if (DB::isManip($query)) {
  169. return DB_OK;
  170. }
  171. $numrows = $this->numrows($result);
  172. if (is_object($numrows)) {
  173. return $numrows;
  174. }
  175. $this->num_rows[$result] = $numrows;
  176. return $result;
  177. }
  178. // }}}
  179. // {{{ nextResult()
  180. /**
  181. * Move the internal fbsql result pointer to the next available result
  182. *
  183. * @param a valid fbsql result resource
  184. *
  185. * @access public
  186. *
  187. * @return true if a result is available otherwise return false
  188. */
  189. function nextResult($result)
  190. {
  191. return @fbsql_next_result($result);
  192. }
  193. // }}}
  194. // {{{ fetchInto()
  195. /**
  196. * Fetch a row and insert the data into an existing array.
  197. *
  198. * Formating of the array and the data therein are configurable.
  199. * See DB_result::fetchInto() for more information.
  200. *
  201. * @param resource $result query result identifier
  202. * @param array $arr (reference) array where data from the row
  203. * should be placed
  204. * @param int $fetchmode how the resulting array should be indexed
  205. * @param int $rownum the row number to fetch
  206. *
  207. * @return mixed DB_OK on success, NULL when end of result set is
  208. * reached or on failure
  209. *
  210. * @see DB_result::fetchInto()
  211. * @access private
  212. */
  213. function fetchInto($result, &$arr, $fetchmode, $rownum=null)
  214. {
  215. if ($rownum !== null) {
  216. if (!@fbsql_data_seek($result, $rownum)) {
  217. return null;
  218. }
  219. }
  220. if ($fetchmode & DB_FETCHMODE_ASSOC) {
  221. $arr = @fbsql_fetch_array($result, FBSQL_ASSOC);
  222. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
  223. $arr = array_change_key_case($arr, CASE_LOWER);
  224. }
  225. } else {
  226. $arr = @fbsql_fetch_row($result);
  227. }
  228. if (!$arr) {
  229. $errno = @fbsql_errno($this->connection);
  230. if (!$errno) {
  231. return NULL;
  232. }
  233. return $this->fbsqlRaiseError($errno);
  234. }
  235. if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
  236. $this->_rtrimArrayValues($arr);
  237. }
  238. if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
  239. $this->_convertNullArrayValuesToEmpty($arr);
  240. }
  241. return DB_OK;
  242. }
  243. // }}}
  244. // {{{ freeResult()
  245. /**
  246. * Free the internal resources associated with $result.
  247. *
  248. * @param $result fbsql result identifier
  249. *
  250. * @access public
  251. *
  252. * @return bool TRUE on success, FALSE if $result is invalid
  253. */
  254. function freeResult($result)
  255. {
  256. return @fbsql_free_result($result);
  257. }
  258. // }}}
  259. // {{{ autoCommit()
  260. function autoCommit($onoff=false)
  261. {
  262. if ($onoff) {
  263. $this->query("SET COMMIT TRUE");
  264. } else {
  265. $this->query("SET COMMIT FALSE");
  266. }
  267. }
  268. // }}}
  269. // {{{ commit()
  270. function commit()
  271. {
  272. fbsql_commit();
  273. }
  274. // }}}
  275. // {{{ rollback()
  276. function rollback()
  277. {
  278. fbsql_rollback();
  279. }
  280. // }}}
  281. // {{{ numCols()
  282. /**
  283. * Get the number of columns in a result set.
  284. *
  285. * @param $result fbsql result identifier
  286. *
  287. * @access public
  288. *
  289. * @return int the number of columns per row in $result
  290. */
  291. function numCols($result)
  292. {
  293. $cols = @fbsql_num_fields($result);
  294. if (!$cols) {
  295. return $this->fbsqlRaiseError();
  296. }
  297. return $cols;
  298. }
  299. // }}}
  300. // {{{ numRows()
  301. /**
  302. * Get the number of rows in a result set.
  303. *
  304. * @param $result fbsql result identifier
  305. *
  306. * @access public
  307. *
  308. * @return int the number of rows in $result
  309. */
  310. function numRows($result)
  311. {
  312. $rows = @fbsql_num_rows($result);
  313. if ($rows === null) {
  314. return $this->fbsqlRaiseError();
  315. }
  316. return $rows;
  317. }
  318. // }}}
  319. // {{{ affectedRows()
  320. /**
  321. * Gets the number of rows affected by the data manipulation
  322. * query. For other queries, this function returns 0.
  323. *
  324. * @return number of rows affected by the last query
  325. */
  326. function affectedRows()
  327. {
  328. if (DB::isManip($this->last_query)) {
  329. $result = @fbsql_affected_rows($this->connection);
  330. } else {
  331. $result = 0;
  332. }
  333. return $result;
  334. }
  335. // }}}
  336. // {{{ errorNative()
  337. /**
  338. * Get the native error code of the last error (if any) that
  339. * occured on the current connection.
  340. *
  341. * @access public
  342. *
  343. * @return int native fbsql error code
  344. */
  345. function errorNative()
  346. {
  347. return fbsql_errno($this->connection);
  348. }
  349. // }}}
  350. // {{{ nextId()
  351. /**
  352. * Returns the next free id in a sequence
  353. *
  354. * @param string $seq_name name of the sequence
  355. * @param boolean $ondemand when true, the seqence is automatically
  356. * created if it does not exist
  357. *
  358. * @return int the next id number in the sequence. DB_Error if problem.
  359. *
  360. * @internal
  361. * @see DB_common::nextID()
  362. * @access public
  363. */
  364. function nextId($seq_name, $ondemand = true)
  365. {
  366. $seqname = $this->getSequenceName($seq_name);
  367. $repeat = 0;
  368. do {
  369. $result = $this->query("INSERT INTO ${seqname} VALUES(NULL)");
  370. if ($ondemand && DB::isError($result) &&
  371. $result->getCode() == DB_ERROR_NOSUCHTABLE) {
  372. $repeat = 1;
  373. $result = $this->createSequence($seq_name);
  374. if (DB::isError($result)) {
  375. return $result;
  376. }
  377. } else {
  378. $repeat = 0;
  379. }
  380. } while ($repeat);
  381. if (DB::isError($result)) {
  382. return $result;
  383. }
  384. return fbsql_insert_id($this->connection);
  385. }
  386. /**
  387. * Creates a new sequence
  388. *
  389. * @param string $seq_name name of the new sequence
  390. *
  391. * @return int DB_OK on success. A DB_Error object is returned if
  392. * problems arise.
  393. *
  394. * @internal
  395. * @see DB_common::createSequence()
  396. * @access public
  397. */
  398. function createSequence($seq_name)
  399. {
  400. $seqname = $this->getSequenceName($seq_name);
  401. return $this->query("CREATE TABLE ${seqname} ".
  402. '(id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'.
  403. ' PRIMARY KEY(id))');
  404. }
  405. // }}}
  406. // {{{ dropSequence()
  407. /**
  408. * Deletes a sequence
  409. *
  410. * @param string $seq_name name of the sequence to be deleted
  411. *
  412. * @return int DB_OK on success. DB_Error if problems.
  413. *
  414. * @internal
  415. * @see DB_common::dropSequence()
  416. * @access public
  417. */
  418. function dropSequence($seq_name)
  419. {
  420. $seqname = $this->getSequenceName($seq_name);
  421. return $this->query("DROP TABLE ${seqname} RESTRICT");
  422. }
  423. // }}}
  424. // {{{ modifyQuery()
  425. function modifyQuery($query)
  426. {
  427. if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) {
  428. // "DELETE FROM table" gives 0 affected rows in fbsql.
  429. // This little hack lets you know how many rows were deleted.
  430. if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
  431. $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
  432. 'DELETE FROM \1 WHERE 1=1', $query);
  433. }
  434. }
  435. return $query;
  436. }
  437. // }}}
  438. // {{{ quoteSmart()
  439. /**
  440. * Format input so it can be safely used in a query
  441. *
  442. * @param mixed $in data to be quoted
  443. *
  444. * @return mixed Submitted variable's type = returned value:
  445. * + null = the string <samp>NULL</samp>
  446. * + boolean = string <samp>TRUE</samp> or <samp>FALSE</samp>
  447. * + integer or double = the unquoted number
  448. * + other (including strings and numeric strings) =
  449. * the data escaped according to MySQL's settings
  450. * then encapsulated between single quotes
  451. *
  452. * @internal
  453. */
  454. function quoteSmart($in)
  455. {
  456. if (is_int($in) || is_double($in)) {
  457. return $in;
  458. } elseif (is_bool($in)) {
  459. return $in ? 'TRUE' : 'FALSE';
  460. } elseif (is_null($in)) {
  461. return 'NULL';
  462. } else {
  463. return "'" . $this->escapeSimple($in) . "'";
  464. }
  465. }
  466. // }}}
  467. // {{{ fbsqlRaiseError()
  468. /**
  469. * Gather information about an error, then use that info to create a
  470. * DB error object and finally return that object.
  471. *
  472. * @param integer $errno PEAR error number (usually a DB constant) if
  473. * manually raising an error
  474. * @return object DB error object
  475. * @see DB_common::errorCode()
  476. * @see DB_common::raiseError()
  477. */
  478. function fbsqlRaiseError($errno = null)
  479. {
  480. if ($errno === null) {
  481. $errno = $this->errorCode(fbsql_errno($this->connection));
  482. }
  483. return $this->raiseError($errno, null, null, null,
  484. fbsql_error($this->connection));
  485. }
  486. // }}}
  487. // {{{ tableInfo()
  488. /**
  489. * Returns information about a table or a result set.
  490. *
  491. * @param object|string $result DB_result object from a query or a
  492. * string containing the name of a table
  493. * @param int $mode a valid tableInfo mode
  494. * @return array an associative array with the information requested
  495. * or an error object if something is wrong
  496. * @access public
  497. * @internal
  498. * @see DB_common::tableInfo()
  499. */
  500. function tableInfo($result, $mode = null) {
  501. if (isset($result->result)) {
  502. /*
  503. * Probably received a result object.
  504. * Extract the result resource identifier.
  505. */
  506. $id = $result->result;
  507. $got_string = false;
  508. } elseif (is_string($result)) {
  509. /*
  510. * Probably received a table name.
  511. * Create a result resource identifier.
  512. */
  513. $id = @fbsql_list_fields($this->dsn['database'],
  514. $result, $this->connection);
  515. $got_string = true;
  516. } else {
  517. /*
  518. * Probably received a result resource identifier.
  519. * Copy it.
  520. * Depricated. Here for compatibility only.
  521. */
  522. $id = $result;
  523. $got_string = false;
  524. }
  525. if (!is_resource($id)) {
  526. return $this->fbsqlRaiseError(DB_ERROR_NEED_MORE_DATA);
  527. }
  528. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
  529. $case_func = 'strtolower';
  530. } else {
  531. $case_func = 'strval';
  532. }
  533. $count = @fbsql_num_fields($id);
  534. // made this IF due to performance (one if is faster than $count if's)
  535. if (!$mode) {
  536. for ($i=0; $i<$count; $i++) {
  537. $res[$i]['table'] = $case_func(@fbsql_field_table($id, $i));
  538. $res[$i]['name'] = $case_func(@fbsql_field_name($id, $i));
  539. $res[$i]['type'] = @fbsql_field_type($id, $i);
  540. $res[$i]['len'] = @fbsql_field_len($id, $i);
  541. $res[$i]['flags'] = @fbsql_field_flags($id, $i);
  542. }
  543. } else { // full
  544. $res["num_fields"]= $count;
  545. for ($i=0; $i<$count; $i++) {
  546. $res[$i]['table'] = $case_func(@fbsql_field_table($id, $i));
  547. $res[$i]['name'] = $case_func(@fbsql_field_name($id, $i));
  548. $res[$i]['type'] = @fbsql_field_type($id, $i);
  549. $res[$i]['len'] = @fbsql_field_len($id, $i);
  550. $res[$i]['flags'] = @fbsql_field_flags($id, $i);
  551. if ($mode & DB_TABLEINFO_ORDER) {
  552. $res['order'][$res[$i]['name']] = $i;
  553. }
  554. if ($mode & DB_TABLEINFO_ORDERTABLE) {
  555. $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  556. }
  557. }
  558. }
  559. // free the result only if we were called on a table
  560. if ($got_string) {
  561. @fbsql_free_result($id);
  562. }
  563. return $res;
  564. }
  565. // }}}
  566. // {{{ getSpecialQuery()
  567. /**
  568. * Returns the query needed to get some backend info
  569. * @param string $type What kind of info you want to retrieve
  570. * @return string The SQL query string
  571. */
  572. function getSpecialQuery($type)
  573. {
  574. switch ($type) {
  575. case 'tables':
  576. return 'select "table_name" from information_schema.tables';
  577. default:
  578. return null;
  579. }
  580. }
  581. // }}}
  582. }
  583. /*
  584. * Local variables:
  585. * tab-width: 4
  586. * c-basic-offset: 4
  587. * End:
  588. */
  589. ?>