odbc.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * The PEAR DB driver for PHP's odbc extension
  5. * for interacting with databases via ODBC connections
  6. *
  7. * PHP version 5
  8. *
  9. * LICENSE: This source file is subject to version 3.0 of the PHP license
  10. * that is available through the world-wide-web at the following URI:
  11. * http://www.php.net/license/3_0.txt. If you did not receive a copy of
  12. * the PHP License and are unable to obtain it through the web, please
  13. * send a note to license@php.net so we can mail you a copy immediately.
  14. *
  15. * @category Database
  16. * @package DB
  17. * @author Stig Bakken <ssb@php.net>
  18. * @author Daniel Convissor <danielc@php.net>
  19. * @copyright 1997-2007 The PHP Group
  20. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  21. * @version CVS: $Id$
  22. * @link http://pear.php.net/package/DB
  23. */
  24. /**
  25. * Obtain the DB_common class so it can be extended from
  26. */
  27. //require_once 'DB/common.php';
  28. require_once 'common.php';
  29. /**
  30. * The methods PEAR DB uses to interact with PHP's odbc extension
  31. * for interacting with databases via ODBC connections
  32. *
  33. * These methods overload the ones declared in DB_common.
  34. *
  35. * More info on ODBC errors could be found here:
  36. * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/trblsql/tr_err_odbc_5stz.asp
  37. *
  38. * @category Database
  39. * @package DB
  40. * @author Stig Bakken <ssb@php.net>
  41. * @author Daniel Convissor <danielc@php.net>
  42. * @copyright 1997-2007 The PHP Group
  43. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  44. * @version Release: 1.9.2
  45. * @link http://pear.php.net/package/DB
  46. */
  47. class DB_odbc extends DB_common
  48. {
  49. // {{{ properties
  50. /**
  51. * The DB driver type (mysql, oci8, odbc, etc.)
  52. * @var string
  53. */
  54. public $phptype = 'odbc';
  55. /**
  56. * The database syntax variant to be used (db2, access, etc.), if any
  57. * @var string
  58. */
  59. public $dbsyntax = 'sql92';
  60. /**
  61. * The capabilities of this DB implementation
  62. *
  63. * The 'new_link' element contains the PHP version that first provided
  64. * new_link support for this DBMS. Contains false if it's unsupported.
  65. *
  66. * Meaning of the 'limit' element:
  67. * + 'emulate' = emulate with fetch row by number
  68. * + 'alter' = alter the query
  69. * + false = skip rows
  70. *
  71. * NOTE: The feature set of the following drivers are different than
  72. * the default:
  73. * + solid: 'transactions' = true
  74. * + navision: 'limit' = false
  75. *
  76. * @var array
  77. */
  78. public $features = array(
  79. 'limit' => 'emulate',
  80. 'new_link' => false,
  81. 'numrows' => true,
  82. 'pconnect' => true,
  83. 'prepare' => false,
  84. 'ssl' => false,
  85. 'transactions' => false,
  86. );
  87. /**
  88. * A mapping of native error codes to DB error codes
  89. * @var array
  90. */
  91. public $errorcode_map = array(
  92. '01004' => DB_ERROR_TRUNCATED,
  93. '07001' => DB_ERROR_MISMATCH,
  94. '21S01' => DB_ERROR_VALUE_COUNT_ON_ROW,
  95. '21S02' => DB_ERROR_MISMATCH,
  96. '22001' => DB_ERROR_INVALID,
  97. '22003' => DB_ERROR_INVALID_NUMBER,
  98. '22005' => DB_ERROR_INVALID_NUMBER,
  99. '22008' => DB_ERROR_INVALID_DATE,
  100. '22012' => DB_ERROR_DIVZERO,
  101. '23000' => DB_ERROR_CONSTRAINT,
  102. '23502' => DB_ERROR_CONSTRAINT_NOT_NULL,
  103. '23503' => DB_ERROR_CONSTRAINT,
  104. '23504' => DB_ERROR_CONSTRAINT,
  105. '23505' => DB_ERROR_CONSTRAINT,
  106. '24000' => DB_ERROR_INVALID,
  107. '34000' => DB_ERROR_INVALID,
  108. '37000' => DB_ERROR_SYNTAX,
  109. '42000' => DB_ERROR_SYNTAX,
  110. '42601' => DB_ERROR_SYNTAX,
  111. 'IM001' => DB_ERROR_UNSUPPORTED,
  112. 'S0000' => DB_ERROR_NOSUCHTABLE,
  113. 'S0001' => DB_ERROR_ALREADY_EXISTS,
  114. 'S0002' => DB_ERROR_NOSUCHTABLE,
  115. 'S0011' => DB_ERROR_ALREADY_EXISTS,
  116. 'S0012' => DB_ERROR_NOT_FOUND,
  117. 'S0021' => DB_ERROR_ALREADY_EXISTS,
  118. 'S0022' => DB_ERROR_NOSUCHFIELD,
  119. 'S1009' => DB_ERROR_INVALID,
  120. 'S1090' => DB_ERROR_INVALID,
  121. 'S1C00' => DB_ERROR_NOT_CAPABLE,
  122. );
  123. /**
  124. * The raw database connection created by PHP
  125. * @var resource
  126. */
  127. public $connection;
  128. /**
  129. * The DSN information for connecting to a database
  130. * @var array
  131. */
  132. public $dsn = array();
  133. /**
  134. * The number of rows affected by a data manipulation query
  135. * @var integer
  136. * @access private
  137. */
  138. public $affected = 0;
  139. // }}}
  140. // {{{ constructor
  141. /**
  142. * This constructor calls <kbd>parent::__construct()</kbd>
  143. *
  144. * @return void
  145. */
  146. public function __construct()
  147. {
  148. parent::__construct();
  149. }
  150. // }}}
  151. // {{{ connect()
  152. /**
  153. * Connect to the database server, log in and open the database
  154. *
  155. * Don't call this method directly. Use DB::connect() instead.
  156. *
  157. * PEAR DB's odbc driver supports the following extra DSN options:
  158. * + cursor The type of cursor to be used for this connection.
  159. *
  160. * @param array $dsn the data source name
  161. * @param bool $persistent should the connection be persistent?
  162. *
  163. * @return int|object
  164. */
  165. public function connect($dsn, $persistent = false)
  166. {
  167. if (!PEAR::loadExtension('odbc')) {
  168. return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
  169. }
  170. $this->dsn = $dsn;
  171. if ($dsn['dbsyntax']) {
  172. $this->dbsyntax = $dsn['dbsyntax'];
  173. }
  174. switch ($this->dbsyntax) {
  175. case 'access':
  176. case 'db2':
  177. case 'solid':
  178. $this->features['transactions'] = true;
  179. break;
  180. case 'navision':
  181. $this->features['limit'] = false;
  182. }
  183. /*
  184. * This is hear for backwards compatibility. Should have been using
  185. * 'database' all along, but prior to 1.6.0RC3 'hostspec' was used.
  186. */
  187. if ($dsn['database']) {
  188. $odbcdsn = $dsn['database'];
  189. } elseif ($dsn['hostspec']) {
  190. $odbcdsn = $dsn['hostspec'];
  191. } else {
  192. $odbcdsn = 'localhost';
  193. }
  194. $connect_function = $persistent ? 'odbc_pconnect' : 'odbc_connect';
  195. if (empty($dsn['cursor'])) {
  196. $this->connection = @$connect_function(
  197. $odbcdsn,
  198. $dsn['username'],
  199. $dsn['password']
  200. );
  201. } else {
  202. $this->connection = @$connect_function(
  203. $odbcdsn,
  204. $dsn['username'],
  205. $dsn['password'],
  206. $dsn['cursor']
  207. );
  208. }
  209. if (!is_resource($this->connection)) {
  210. return $this->raiseError(
  211. DB_ERROR_CONNECT_FAILED,
  212. null,
  213. null,
  214. null,
  215. $this->errorNative()
  216. );
  217. }
  218. return DB_OK;
  219. }
  220. // }}}
  221. // {{{ disconnect()
  222. /**
  223. * Gets the DBMS' native error code and message produced by the last query
  224. *
  225. * @return string the DBMS' error code and message
  226. */
  227. public function errorNative()
  228. {
  229. if (!is_resource($this->connection)) {
  230. return @odbc_error() . ' ' . @odbc_errormsg();
  231. }
  232. return @odbc_error($this->connection) . ' ' . @odbc_errormsg($this->connection);
  233. }
  234. // }}}
  235. // {{{ simpleQuery()
  236. /**
  237. * Disconnects from the database server
  238. *
  239. * @return bool|void
  240. */
  241. public function disconnect()
  242. {
  243. $err = @odbc_close($this->connection);
  244. $this->connection = null;
  245. return $err;
  246. }
  247. // }}}
  248. // {{{ nextResult()
  249. /**
  250. * Sends a query to the database server
  251. *
  252. * @param string the SQL query string
  253. *
  254. * @return mixed + a PHP result resrouce for successful SELECT queries
  255. * + the DB_OK constant for other successful queries
  256. * + a DB_Error object on failure
  257. */
  258. public function simpleQuery($query)
  259. {
  260. $this->last_query = $query;
  261. $query = $this->modifyQuery($query);
  262. $result = @odbc_exec($this->connection, $query);
  263. if (!$result) {
  264. return $this->odbcRaiseError(); // XXX ERRORMSG
  265. }
  266. // Determine which queries that should return data, and which
  267. // should return an error code only.
  268. if ($this->_checkManip($query)) {
  269. $this->affected = $result; // For affectedRows()
  270. return DB_OK;
  271. }
  272. $this->affected = 0;
  273. return $result;
  274. }
  275. // }}}
  276. // {{{ fetchInto()
  277. /**
  278. * Produces a DB_Error object regarding the current problem
  279. *
  280. * @param int $errno if the error is being manually raised pass a
  281. * DB_ERROR* constant here. If this isn't passed
  282. * the error information gathered from the DBMS.
  283. *
  284. * @return object the DB_Error object
  285. *
  286. * @see DB_common::raiseError(),
  287. * DB_odbc::errorNative(), DB_common::errorCode()
  288. */
  289. public function odbcRaiseError($errno = null)
  290. {
  291. if ($errno === null) {
  292. switch ($this->dbsyntax) {
  293. case 'access':
  294. if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
  295. $this->errorcode_map['07001'] = DB_ERROR_NOSUCHFIELD;
  296. } else {
  297. // Doing this in case mode changes during runtime.
  298. $this->errorcode_map['07001'] = DB_ERROR_MISMATCH;
  299. }
  300. $native_code = odbc_error($this->connection);
  301. // S1000 is for "General Error." Let's be more specific.
  302. if ($native_code == 'S1000') {
  303. $errormsg = odbc_errormsg($this->connection);
  304. static $error_regexps;
  305. if (!isset($error_regexps)) {
  306. $error_regexps = array(
  307. '/includes related records.$/i' => DB_ERROR_CONSTRAINT,
  308. '/cannot contain a Null value/i' => DB_ERROR_CONSTRAINT_NOT_NULL,
  309. );
  310. }
  311. foreach ($error_regexps as $regexp => $code) {
  312. if (preg_match($regexp, $errormsg)) {
  313. return $this->raiseError(
  314. $code,
  315. null,
  316. null,
  317. null,
  318. $native_code . ' ' . $errormsg
  319. );
  320. }
  321. }
  322. $errno = DB_ERROR;
  323. } else {
  324. $errno = $this->errorCode($native_code);
  325. }
  326. break;
  327. default:
  328. $errno = $this->errorCode(odbc_error($this->connection));
  329. }
  330. }
  331. return $this->raiseError(
  332. $errno,
  333. null,
  334. null,
  335. null,
  336. $this->errorNative()
  337. );
  338. }
  339. // }}}
  340. // {{{ freeResult()
  341. /**
  342. * Move the internal odbc result pointer to the next available result
  343. *
  344. * @param a valid fbsql result resource
  345. *
  346. * @access public
  347. *
  348. * @return true if a result is available otherwise return false
  349. */
  350. public function nextResult($result)
  351. {
  352. return @odbc_next_result($result);
  353. }
  354. // }}}
  355. // {{{ numCols()
  356. /**
  357. * Places a row from the result set into the given array
  358. *
  359. * Formating of the array and the data therein are configurable.
  360. * See DB_result::fetchInto() for more information.
  361. *
  362. * This method is not meant to be called directly. Use
  363. * DB_result::fetchInto() instead. It can't be declared "protected"
  364. * because DB_result is a separate object.
  365. *
  366. * @param resource $result the query result resource
  367. * @param array $arr the referenced array to put the data in
  368. * @param int $fetchmode how the resulting array should be indexed
  369. * @param int $rownum the row number to fetch (0 = first row)
  370. *
  371. * @return mixed DB_OK on success, NULL when the end of a result set is
  372. * reached or on failure
  373. *
  374. * @see DB_result::fetchInto()
  375. */
  376. public function fetchInto($result, &$arr, $fetchmode, $rownum = null)
  377. {
  378. $arr = array();
  379. if ($rownum !== null) {
  380. $rownum++; // ODBC first row is 1
  381. if (version_compare(phpversion(), '4.2.0', 'ge')) {
  382. $cols = @odbc_fetch_into($result, $arr, $rownum);
  383. } else {
  384. $cols = @odbc_fetch_into($result, $rownum, $arr);
  385. }
  386. } else {
  387. $cols = @odbc_fetch_into($result, $arr);
  388. }
  389. if (!$cols) {
  390. return null;
  391. }
  392. if ($fetchmode !== DB_FETCHMODE_ORDERED) {
  393. for ($i = 0; $i < count($arr); $i++) {
  394. $colName = @odbc_field_name($result, $i + 1);
  395. $a[$colName] = $arr[$i];
  396. }
  397. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
  398. $a = array_change_key_case($a, CASE_LOWER);
  399. }
  400. $arr = $a;
  401. }
  402. if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
  403. $this->_rtrimArrayValues($arr);
  404. }
  405. if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
  406. $this->_convertNullArrayValuesToEmpty($arr);
  407. }
  408. return DB_OK;
  409. }
  410. // }}}
  411. // {{{ affectedRows()
  412. /**
  413. * Deletes the result set and frees the memory occupied by the result set
  414. *
  415. * This method is not meant to be called directly. Use
  416. * DB_result::free() instead. It can't be declared "protected"
  417. * because DB_result is a separate object.
  418. *
  419. * @param resource $result PHP's query result resource
  420. *
  421. * @return bool TRUE on success, FALSE if $result is invalid
  422. *
  423. * @see DB_result::free()
  424. */
  425. public function freeResult($result)
  426. {
  427. return is_resource($result) ? odbc_free_result($result) : false;
  428. }
  429. // }}}
  430. // {{{ numRows()
  431. /**
  432. * Gets the number of columns in a result set
  433. *
  434. * This method is not meant to be called directly. Use
  435. * DB_result::numCols() instead. It can't be declared "protected"
  436. * because DB_result is a separate object.
  437. *
  438. * @param resource $result PHP's query result resource
  439. *
  440. * @return int|object
  441. *
  442. * @see DB_result::numCols()
  443. */
  444. public function numCols($result)
  445. {
  446. $cols = @odbc_num_fields($result);
  447. if (!$cols) {
  448. return $this->odbcRaiseError();
  449. }
  450. return $cols;
  451. }
  452. // }}}
  453. // {{{ quoteIdentifier()
  454. /**
  455. * Determines the number of rows affected by a data maniuplation query
  456. *
  457. * 0 is returned for queries that don't manipulate data.
  458. *
  459. * @return int|object
  460. */
  461. public function affectedRows()
  462. {
  463. if (empty($this->affected)) { // In case of SELECT stms
  464. return 0;
  465. }
  466. $nrows = @odbc_num_rows($this->affected);
  467. if ($nrows == -1) {
  468. return $this->odbcRaiseError();
  469. }
  470. return $nrows;
  471. }
  472. // }}}
  473. // {{{ nextId()
  474. /**
  475. * Gets the number of rows in a result set
  476. *
  477. * Not all ODBC drivers support this functionality. If they don't
  478. * a DB_Error object for DB_ERROR_UNSUPPORTED is returned.
  479. *
  480. * This method is not meant to be called directly. Use
  481. * DB_result::numRows() instead. It can't be declared "protected"
  482. * because DB_result is a separate object.
  483. *
  484. * @param resource $result PHP's query result resource
  485. *
  486. * @return int|object
  487. *
  488. * @see DB_result::numRows()
  489. */
  490. public function numRows($result)
  491. {
  492. $nrows = @odbc_num_rows($result);
  493. if ($nrows == -1) {
  494. return $this->odbcRaiseError(DB_ERROR_UNSUPPORTED);
  495. }
  496. if ($nrows === false) {
  497. return $this->odbcRaiseError();
  498. }
  499. return $nrows;
  500. }
  501. /**
  502. * Quotes a string so it can be safely used as a table or column name
  503. *
  504. * Use 'mssql' as the dbsyntax in the DB DSN only if you've unchecked
  505. * "Use ANSI quoted identifiers" when setting up the ODBC data source.
  506. *
  507. * @param string $str identifier name to be quoted
  508. *
  509. * @return string quoted identifier string
  510. *
  511. * @see DB_common::quoteIdentifier()
  512. * @since Method available since Release 1.6.0
  513. */
  514. public function quoteIdentifier($str)
  515. {
  516. switch ($this->dsn['dbsyntax']) {
  517. case 'access':
  518. return '[' . $str . ']';
  519. case 'mssql':
  520. case 'sybase':
  521. return '[' . str_replace(']', ']]', $str) . ']';
  522. case 'mysql':
  523. case 'mysqli':
  524. return '`' . $str . '`';
  525. default:
  526. return '"' . str_replace('"', '""', $str) . '"';
  527. }
  528. }
  529. // }}}
  530. // {{{ dropSequence()
  531. /**
  532. * Returns the next free id in a sequence
  533. *
  534. * @param string $seq_name name of the sequence
  535. * @param boolean $ondemand when true, the seqence is automatically
  536. * created if it does not exist
  537. *
  538. * @return int|object
  539. * A DB_Error object on failure.
  540. *
  541. * @see DB_common::nextID(), DB_common::getSequenceName(),
  542. * DB_odbc::createSequence(), DB_odbc::dropSequence()
  543. */
  544. public function nextId($seq_name, $ondemand = true)
  545. {
  546. $seqname = $this->getSequenceName($seq_name);
  547. $repeat = 0;
  548. do {
  549. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  550. $result = $this->query("update ${seqname} set id = id + 1");
  551. $this->popErrorHandling();
  552. if ($ondemand && DB::isError($result) &&
  553. $result->getCode() == DB_ERROR_NOSUCHTABLE) {
  554. $repeat = 1;
  555. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  556. $result = $this->createSequence($seq_name);
  557. $this->popErrorHandling();
  558. if (DB::isError($result)) {
  559. return $this->raiseError($result);
  560. }
  561. $result = $this->query("insert into ${seqname} (id) values(0)");
  562. } else {
  563. $repeat = 0;
  564. }
  565. } while ($repeat);
  566. if (DB::isError($result)) {
  567. return $this->raiseError($result);
  568. }
  569. $result = $this->query("select id from ${seqname}");
  570. if (DB::isError($result)) {
  571. return $result;
  572. }
  573. $row = $result->fetchRow(DB_FETCHMODE_ORDERED);
  574. if (DB::isError($row || !$row)) {
  575. return $row;
  576. }
  577. return $row[0];
  578. }
  579. // }}}
  580. // {{{ autoCommit()
  581. /**
  582. * Creates a new sequence
  583. *
  584. * @param string $seq_name name of the new sequence
  585. *
  586. * @return int DB_OK on success. A DB_Error object on failure.
  587. *
  588. * @see DB_common::createSequence(), DB_common::getSequenceName(),
  589. * DB_odbc::nextID(), DB_odbc::dropSequence()
  590. */
  591. public function createSequence($seq_name)
  592. {
  593. return $this->query('CREATE TABLE '
  594. . $this->getSequenceName($seq_name)
  595. . ' (id integer NOT NULL,'
  596. . ' PRIMARY KEY(id))');
  597. }
  598. // }}}
  599. // {{{ commit()
  600. /**
  601. * Deletes a sequence
  602. *
  603. * @param string $seq_name name of the sequence to be deleted
  604. *
  605. * @return int DB_OK on success. A DB_Error object on failure.
  606. *
  607. * @see DB_common::dropSequence(), DB_common::getSequenceName(),
  608. * DB_odbc::nextID(), DB_odbc::createSequence()
  609. */
  610. public function dropSequence($seq_name)
  611. {
  612. return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
  613. }
  614. // }}}
  615. // {{{ rollback()
  616. /**
  617. * Enables or disables automatic commits
  618. *
  619. * @param bool $onoff true turns it on, false turns it off
  620. *
  621. * @return int|object
  622. * doesn't support auto-committing transactions.
  623. */
  624. public function autoCommit($onoff = false)
  625. {
  626. if (!@odbc_autocommit($this->connection, $onoff)) {
  627. return $this->odbcRaiseError();
  628. }
  629. return DB_OK;
  630. }
  631. // }}}
  632. // {{{ odbcRaiseError()
  633. /**
  634. * Commits the current transaction
  635. *
  636. * @return int|object
  637. */
  638. public function commit()
  639. {
  640. if (!@odbc_commit($this->connection)) {
  641. return $this->odbcRaiseError();
  642. }
  643. return DB_OK;
  644. }
  645. // }}}
  646. // {{{ errorNative()
  647. /**
  648. * Reverts the current transaction
  649. *
  650. * @return int|object
  651. */
  652. public function rollback()
  653. {
  654. if (!@odbc_rollback($this->connection)) {
  655. return $this->odbcRaiseError();
  656. }
  657. return DB_OK;
  658. }
  659. // }}}
  660. // {{{ tableInfo()
  661. /**
  662. * Returns information about a table or a result set
  663. *
  664. * @param object|string $result DB_result object from a query or a
  665. * string containing the name of a table.
  666. * While this also accepts a query result
  667. * resource identifier, this behavior is
  668. * deprecated.
  669. * @param int $mode a valid tableInfo mode
  670. *
  671. * @return array|object
  672. * A DB_Error object on failure.
  673. *
  674. * @see DB_common::tableInfo()
  675. * @since Method available since Release 1.7.0
  676. */
  677. public function tableInfo($result, $mode = null)
  678. {
  679. if (is_string($result)) {
  680. /*
  681. * Probably received a table name.
  682. * Create a result resource identifier.
  683. */
  684. $id = @odbc_exec($this->connection, "SELECT * FROM $result");
  685. if (!$id) {
  686. return $this->odbcRaiseError();
  687. }
  688. $got_string = true;
  689. } elseif (isset($result->result)) {
  690. /*
  691. * Probably received a result object.
  692. * Extract the result resource identifier.
  693. */
  694. $id = $result->result;
  695. $got_string = false;
  696. } else {
  697. /*
  698. * Probably received a result resource identifier.
  699. * Copy it.
  700. * Deprecated. Here for compatibility only.
  701. */
  702. $id = $result;
  703. $got_string = false;
  704. }
  705. if (!is_resource($id)) {
  706. return $this->odbcRaiseError(DB_ERROR_NEED_MORE_DATA);
  707. }
  708. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
  709. $case_func = 'strtolower';
  710. } else {
  711. $case_func = 'strval';
  712. }
  713. $count = @odbc_num_fields($id);
  714. $res = array();
  715. if ($mode) {
  716. $res['num_fields'] = $count;
  717. }
  718. for ($i = 0; $i < $count; $i++) {
  719. $col = $i + 1;
  720. $res[$i] = array(
  721. 'table' => $got_string ? $case_func($result) : '',
  722. 'name' => $case_func(@odbc_field_name($id, $col)),
  723. 'type' => @odbc_field_type($id, $col),
  724. 'len' => @odbc_field_len($id, $col),
  725. 'flags' => '',
  726. );
  727. if ($mode & DB_TABLEINFO_ORDER) {
  728. $res['order'][$res[$i]['name']] = $i;
  729. }
  730. if ($mode & DB_TABLEINFO_ORDERTABLE) {
  731. $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  732. }
  733. }
  734. // free the result only if we were called on a table
  735. if ($got_string) {
  736. @odbc_free_result($id);
  737. }
  738. return $res;
  739. }
  740. // }}}
  741. // {{{ getSpecialQuery()
  742. /**
  743. * Obtains the query string needed for listing a given type of objects
  744. *
  745. * Thanks to symbol1@gmail.com and Philippe.Jausions@11abacus.com.
  746. *
  747. * @param string $type the kind of objects you want to retrieve
  748. *
  749. * @return array|string
  750. *
  751. * @access protected
  752. * @see DB_common::getListOf()
  753. * @since Method available since Release 1.7.0
  754. */
  755. public function getSpecialQuery($type)
  756. {
  757. switch ($type) {
  758. case 'databases':
  759. if (!function_exists('odbc_data_source')) {
  760. return null;
  761. }
  762. $res = @odbc_data_source($this->connection, SQL_FETCH_FIRST);
  763. if (is_array($res)) {
  764. $out = array($res['server']);
  765. while ($res = @odbc_data_source(
  766. $this->connection,
  767. SQL_FETCH_NEXT
  768. )) {
  769. $out[] = $res['server'];
  770. }
  771. return $out;
  772. } else {
  773. return $this->odbcRaiseError();
  774. }
  775. break;
  776. case 'tables':
  777. case 'schema.tables':
  778. $keep = 'TABLE';
  779. break;
  780. case 'views':
  781. $keep = 'VIEW';
  782. break;
  783. default:
  784. return null;
  785. }
  786. /*
  787. * Removing non-conforming items in the while loop rather than
  788. * in the odbc_tables() call because some backends choke on this:
  789. * odbc_tables($this->connection, '', '', '', 'TABLE')
  790. */
  791. $res = @odbc_tables($this->connection);
  792. if (!$res) {
  793. return $this->odbcRaiseError();
  794. }
  795. $out = array();
  796. while ($row = odbc_fetch_array($res)) {
  797. if ($row['TABLE_TYPE'] != $keep) {
  798. continue;
  799. }
  800. if ($type == 'schema.tables') {
  801. $out[] = $row['TABLE_SCHEM'] . '.' . $row['TABLE_NAME'];
  802. } else {
  803. $out[] = $row['TABLE_NAME'];
  804. }
  805. }
  806. return $out;
  807. }
  808. // }}}
  809. }
  810. /*
  811. * Local variables:
  812. * tab-width: 4
  813. * c-basic-offset: 4
  814. * End:
  815. */