PageRenderTime 74ms CodeModel.GetById 41ms RepoModel.GetById 0ms app.codeStats 0ms

/php_support/dbsource.inc

http://perseph.googlecode.com/
PHP | 1257 lines | 863 code | 213 blank | 181 comment | 75 complexity | d4153a25ce089f9aa71d534505e52d31 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-3.0
  1. <?php
  2. /* ***** BEGIN LICENSE BLOCK *****
  3. * Version: LGPL 3.0
  4. * This file is part of Persephone's output and/or part of Persephone.
  5. *
  6. * This file is an exception to the main Persephone license in that
  7. * this file may be redistributed under the terms of the GNU
  8. * Lesser General Public License, version 3.
  9. *
  10. * Contributors:
  11. * edA-qa mort-ora-y <edA-qa@disemia.com>
  12. * ***** END LICENSE BLOCK ***** */
  13. @require_once 'MDB2.php';
  14. @require_once 'MDB2/Date.php';
  15. //@require_once 'Date.php';
  16. interface DBSource {
  17. /**
  18. * Used to perform read-only queries from the DB which
  19. * return data.
  20. */
  21. public function query( $q );
  22. /**
  23. * For write operations.
  24. */
  25. public function exec( $q );
  26. public function fetchRow( $res );
  27. public function getResult( $res, $name );
  28. public function decode( $value, $type );
  29. public function quote( $value, $type );
  30. public function endResult( $res );
  31. /**
  32. * Escapes a column name for use in SQL. The name given as a parameter
  33. * may be a structured name, such as "db.table" in which case it must be
  34. * escaped appropriately (usually in parts). This is of course means that there
  35. * is no proper way to escape .'s for now...
  36. */
  37. public function escapeColumn( $column );
  38. public function escapeTable( $table );
  39. public function getNewID( $table = null, $field = null );
  40. /**
  41. * Allows the disabling (or reenabling) of error logging. By default DB errors
  42. * will be logged using error_log
  43. */
  44. public function setErrorLogging( $on );
  45. /**
  46. * Returns the textual form of the last error which occurred.
  47. */
  48. public function getLastError( );
  49. }
  50. class MDB2DBSource implements DBSource {
  51. private $mdb;
  52. private $textType;
  53. private $timezone;
  54. /**
  55. *
  56. * @param textType [in] speciifying a type other than 'text' allows you to use a custom
  57. * custom text conversion, useful in cases where the backend/transport is not
  58. * charset aware and you need special conversions. Refer to the alltests.php for an
  59. * example of this.
  60. * NOTE: If your transport is character set aware, then you can use the setCharset
  61. * function of MDB2 and use the default 'text' type instead.
  62. */
  63. public function __construct( MDB2_Driver_Common &$mdb, $textType = 'text' ) {
  64. $this->mdb =& $mdb;
  65. $this->textType = $textType;
  66. $this->timezone = new DateTimeZone( date_default_timezone_get() );
  67. }
  68. public function &getMDB() {
  69. return $this->mdb;
  70. }
  71. public function setTimezone( $tz ) {
  72. if( !($tz instanceof DateTimeZone) )
  73. $tz = new DateTimeZone( $tz );
  74. $this->timezone = $tz;
  75. }
  76. public function getTimezone( $tz ) {
  77. return $this->timezone;
  78. }
  79. /**
  80. * This function is mainly meant for the MDB2 direct mode in persephone to cache
  81. * a DBSource object but replace the MDB2.
  82. */
  83. public function switchMDB( MDB2_Driver_Common &$mdb ) {
  84. $this->mdb =& $mdb;
  85. }
  86. public function checkError( &$item ) {
  87. @$failed = PEAR::isError( $item );
  88. if( !$failed )
  89. return true;
  90. $this->lastError = $item->getMessage();
  91. if( $this->errorLogging )
  92. error_log( $this->lastError );
  93. return false;
  94. }
  95. private $errorLogging = true;
  96. public function setErrorLogging( $on ) {
  97. $this->errorLogging = $on;
  98. }
  99. private $lastError;
  100. public function getLastError( ) {
  101. return $this->lastError;
  102. }
  103. public function query( $q ) {
  104. $res = $this->mdb->query( $q );
  105. if( !$this->checkError( $res ) )
  106. return false;
  107. return new MDB2Result( $res );
  108. }
  109. public function exec( $q ) {
  110. $res = $this->mdb->exec( $q );
  111. if( !$this->checkError( $res ) )
  112. return false;
  113. return $res;
  114. }
  115. public function fetchRow( $res ) {
  116. //to allow people not to check the result of query
  117. if( $res === false )
  118. return false;
  119. $res->currow = $res->mdbres->fetchRow(MDB2_FETCHMODE_ASSOC);
  120. if( $res->currow === null )
  121. return false;
  122. return $this->checkError( $res->currow );
  123. }
  124. public function getResult( $res, $name ) {
  125. return $res->currow[strtolower($name)];
  126. }
  127. const TEXTTYPE = '_textType';
  128. static private $typeMap = array(
  129. 'string' => self::TEXTTYPE,
  130. 'text' => self::TEXTTYPE,
  131. 'decimal' => 'decimal',
  132. 'integer' => 'integer',
  133. 'float' => 'float',
  134. 'date' => 'date',
  135. 'time' => 'time',
  136. 'datetime' => 'timestamp',
  137. 'bool' => 'boolean'
  138. );
  139. private function getMDBType( $type ) {
  140. $type = strtolower( $type );
  141. if( !isset( self::$typeMap[$type] ) )
  142. throw new Exception( "Unsupported quote type: $type" );
  143. $type = self::$typeMap[$type];
  144. if( $type === self::TEXTTYPE )
  145. return $this->textType;
  146. return $type;
  147. }
  148. public function decode( $value, $type ) {
  149. $mtype = $this->getMDBType( $type );
  150. //datatype may not yet be initialized (lazy init with quote it appears)
  151. if( !isset( $this->mdb->datatype ) )
  152. $this->mdb->loadModule('Datatype', null, true);
  153. $halfraw = $this->mdb->datatype->convertResult( $value, $mtype );
  154. if( $value === null )
  155. return $value;
  156. switch( strtolower( $type ) ) {
  157. case 'decimal':
  158. $out = floatval( $halfraw ); //MDB leaves them as strings (probably correct, but well...)
  159. break;
  160. case 'date':
  161. $out = $this->mdb2_DateToDate( $value );
  162. break;
  163. case 'datetime':
  164. $out = $this->mdb2_DateTimeToDateTime( $value );
  165. break;
  166. case 'time':
  167. $out = _dbs_decode_time( $value );
  168. break;
  169. default:
  170. $out = $halfraw;
  171. break;
  172. }
  173. return $out;
  174. }
  175. public function quote( $value, $type ) {
  176. $mtype = $this->getMDBType( $type );
  177. if( $value === null ) {
  178. $halfraw = null;
  179. } else {
  180. switch( strtolower( $type ) ) {
  181. case 'date':
  182. $halfraw = $this->dateToMDB2_Date( $value );
  183. break;
  184. case 'datetime':
  185. $halfraw = $this->dateTimeToMDB2_DateTime( $value );
  186. break;
  187. case 'time':
  188. $halfraw = _dbs_encode_time( $value );
  189. break;
  190. default:
  191. $halfraw = $value;
  192. break;
  193. }
  194. }
  195. $q = $this->mdb->quote( $halfraw, $mtype );
  196. if( $this->checkError( $q ) )
  197. return $q;
  198. throw new Exception( "Could not convert type/value: $type" );
  199. }
  200. public function endResult( $res ) {
  201. if( $res instanceof MDB2Result )
  202. $res->mdbres->free();
  203. }
  204. public function escapeColumn( $column ) {
  205. return $this->mdb->quoteIdentifier( $column );
  206. }
  207. public function escapeTable( $table ) {
  208. return $this->mdb->quoteIdentifier( $table );
  209. }
  210. public function getNewID( $table = null, $field = null ) {
  211. return $this->mdb->lastInsertID( $table, $field );
  212. }
  213. public function dateToMDB2_Date( $date ) {
  214. //ignore timezone on the date
  215. return $date->format( 'Y-m-d' ); //though the default, be explicit, this is what MDB2 timestamps are
  216. }
  217. public function dateTimeToMDB2_DateTime( $date ) {
  218. $ndate = clone $date;
  219. $ndate->setTimezone( $this->timezone );
  220. return $ndate->format( 'Y-m-d H:i:s' ); //though the default, be explicit, this is what MDB2 timestamps are
  221. }
  222. public function mdb2_DateTimeToDateTime( $mdate ) {
  223. return new DateTime( $mdate, $this->timezone );
  224. }
  225. public function mdb2_DateToDate( $mdate ) {
  226. return new DateTime( $mdate );
  227. }
  228. }
  229. class MDB2Result {
  230. public $mdbres;
  231. public $currow;
  232. public function __construct( &$mdbres ) {
  233. $this->mdbres =& $mdbres;
  234. }
  235. }
  236. ///////////////////////////////////////////////////////////////////////////////////////////////////
  237. // DEPRACATED WARNING
  238. //
  239. // The classes below expose more than they should according to
  240. // the interface. These need to be cleaned up and/or replaced.
  241. // The MDB2 method is likely to be the long-term preferred option.
  242. /* The class structure seems a little unusual here, each fucntion the parent
  243. * explicitly calls the child class function. This is since the parent class
  244. * is really the main class, and the child is just a Driver, not really meant
  245. * to be called directly -- using the same mechanism for all functions
  246. * allows consistancy.
  247. */
  248. abstract class DBSourceBase implements DBSource {
  249. var $connect;
  250. var $isinit;
  251. var $str_enc; //the string encoding used in the DB (what is returned raw)
  252. private $timezone;
  253. public function setTimezone( $tz ) {
  254. if( !($tz instanceof DateTimeZone) )
  255. $tz = new DateTimeZone( $tz );
  256. $this->timezone = $tz;
  257. }
  258. public function getTimezone( $tz ) {
  259. return $this->timezone;
  260. }
  261. /* The default encoding is Windows-1252 since most of the system have an
  262. * Access front-end which seems to be using this encoding, at least initially
  263. *
  264. * PHP: Should be "protected", but then can't have public in derived classes
  265. */
  266. public function __construct( $astr_enc = "windows-1252" ) {
  267. $this->connect = NULL;
  268. $this->isinit = False;
  269. $this->str_enc = $astr_enc;
  270. $this->timezone = new DateTimeZone( date_default_timezone_get() );
  271. }
  272. public function fetchRow( $res ) {
  273. return $this->fetch_row( $res );
  274. }
  275. public function endResult( $res ) {
  276. return $this->endresult( $res );
  277. }
  278. function init( ) {
  279. if( $this->isinit )
  280. return;
  281. $this->connect = $this->int_connect();
  282. if( $this->connect == 0 )
  283. die( "Cannot connect to " . $this->name );
  284. $this->isinit = True;
  285. }
  286. function deinit( ) {
  287. if( $this->isinit ) {
  288. $this->int_close();
  289. $this->isinit = False;
  290. }
  291. }
  292. /**
  293. * Wrapper to execute queries by calling the exec method
  294. *
  295. * The exec method of the driver class is called to execute this
  296. * function.
  297. *
  298. * If an error occurs the program will break (by calling the
  299. * Error_handler)
  300. */
  301. function query( $q ) {
  302. $this->init();
  303. $ret = $this->int_exec( $q );
  304. if( $ret === false ) {
  305. $this->lastError = "Could not execute query: $q: " . $this->int_errormsg();
  306. if( $this->errorLogging )
  307. error_log( $this->lastError );
  308. }
  309. return $ret;
  310. }
  311. private $errorLogging = true;
  312. public function setErrorLogging( $on ) {
  313. $this->errorLogging = $on;
  314. }
  315. private $lastError;
  316. public function getLastError( ) {
  317. return $this->lastError;
  318. }
  319. function exec( $q ) {
  320. return $this->query( $q );
  321. }
  322. function getResult( $res, $col ) {
  323. return $this->int_result( $res, $col );
  324. }
  325. /**
  326. * @deprecated use getResult
  327. */
  328. function result( $res, $col ) {
  329. return $this->int_result( $res, $col );
  330. }
  331. /**
  332. * @return [out] reference to a created result set. Each call
  333. * to fetch_resultset produces a new copy
  334. */
  335. function &fetch_resultset( $res, $rowNum = null ) {
  336. //map to new unified function with appropriate params
  337. return $this->allresults( $res,
  338. array(
  339. 'row_name' => $rowNum
  340. ) );
  341. }
  342. function &allresults( $res, $opts ) {
  343. return $this->int_allresults( $res, $opts );
  344. }
  345. function fetch_row( $res ) {
  346. return $this->int_fetch_row( $res );
  347. }
  348. /**
  349. * Helper function, since we don't know which encoding the DB has
  350. * this converets from DB encoding to PHP string encoding.
  351. *
  352. * @deprecated use decode (though internal still uses this)
  353. */
  354. function decode_str( $str ) {
  355. return mb_convert_encoding( $str, mb_internal_encoding(), $this->str_enc );
  356. }
  357. /**
  358. * @deprecated use quote
  359. */
  360. function encode_str( $str ) {
  361. return mb_convert_encoding( $str, $this->str_enc );
  362. }
  363. /**
  364. * @deprecated use quote
  365. */
  366. function escape_sql_str( $str ) {
  367. die("Unsupported!");
  368. }
  369. /**
  370. * @deprecated use quote
  371. */
  372. function escape_encode_str( $str ) {
  373. return $this->escape_sql_str( $this->encode_str( $str ) );
  374. }
  375. function escape_sql_blob( $data ) {
  376. die("Unsupported!");
  377. }
  378. public function getNewID( $table = null, $field = null ) {
  379. die("Unsupported!");
  380. }
  381. function escapeColumn( $column ) {
  382. die("Unsupported!");
  383. }
  384. public function escapeTable( $table ) {
  385. return $this->escapeColumn( $table );
  386. }
  387. /**
  388. * Returns a bracketed clause for the specified ids (as numbers)
  389. *
  390. * @param ids [in] array of ids
  391. * @return [out] SQL bracketed,comma'd clause
  392. *
  393. * @deprecated use the SQL class or something else
  394. */
  395. function get_in_for_ids( $ids ) {
  396. $ret = '(';
  397. $c = 0;
  398. foreach( $ids as $id ) {
  399. if( $c )
  400. $ret .= ',';
  401. $ret .= $id;
  402. $c++;
  403. }
  404. $ret .= ')';
  405. return $ret;
  406. }
  407. //MDB2 like interface, used in the DBSchema code
  408. function quote( $value, $type ) {
  409. if( $value === null )
  410. return 'NULL';
  411. switch( strtolower( $type ) ) {
  412. case 'string':
  413. case 'text':
  414. return '\'' . $this->escape_encode_str( $value ) . '\'';
  415. case 'integer':
  416. return intval( $value );
  417. case 'decimal':
  418. case 'float':
  419. return floatval( $value );
  420. case 'datetime': {
  421. $nvalue = clone $value;
  422. $nvalue->setTimezone( $this->timezone );
  423. return '\'' . $nvalue->format( "Y-m-d H:i:s" ) . '\'';
  424. } case 'date':
  425. return '\'' . $value->format( "Y-m-d" ) . '\'';
  426. case 'time':
  427. return '\'' . _dbs_encode_time( $value ) . '\'';
  428. case 'bool':
  429. return $value ? 1 : 0; //TODO: MYSQL/Oracle specific, Postgres uses True/false!
  430. default:
  431. throw new Exception( "Unrecognized quote type: $type" );
  432. }
  433. }
  434. function decode( $value, $type ) {
  435. if( $value === null )
  436. return null;
  437. switch( strtolower( $type ) ) {
  438. case 'string':
  439. case 'text':
  440. return $this->decode_str( $value );
  441. case 'integer':
  442. return intval( $value );
  443. case 'decimal':
  444. case 'float':
  445. return floatval( $value );
  446. case 'date':
  447. //use local timezone so that the date porition is left as-is, without a proper Date class we can't do anything else
  448. return new DateTime( $value );
  449. case 'datetime':
  450. //remove trailing milliseconds if there (quick and dirty... *sigh* new PHP should parse them)
  451. $dot = strrpos( $value, '.' );
  452. if( $dot !== false )
  453. $value = substr( $value, 0, $dot );
  454. $ret = new DateTime( $value, $this->timezone );
  455. return $ret;
  456. case 'time':
  457. return _dbs_decode_time( $value );
  458. case 'bool':
  459. return $value == '1' || strtolower( $value ) === 'true';
  460. default:
  461. throw new Exception( "Unrecognized decode type: $type" );
  462. }
  463. }
  464. function result_query( $query ) {
  465. return new DBResult( $this, $this->query( $query ) );
  466. }
  467. }
  468. function _dbs_decode_time( $value ) {
  469. if( !strlen( $value ) )
  470. return $value;
  471. if( $value[0] == '-' ) {
  472. $neg = true;
  473. $value = substr( $value, 1 );
  474. } else
  475. $neg = false;
  476. $parts = explode( ':', $value );
  477. if( count( $parts ) != 3 )
  478. return null;
  479. $ret = $parts[0] * 3600 + $parts[1] * 60 + $parts[2];
  480. if( $neg )
  481. $ret = -$ret;
  482. return $ret;
  483. }
  484. function _dbs_encode_time( $value ) {
  485. if( $value < 0 ) {
  486. $neg = true;
  487. $value = -$value;
  488. } else
  489. $neg = false;
  490. $ret = sprintf( "%d:%02d:%02d", floor($value/3600), floor($value/60) % 60, $value % 60 );
  491. if( $neg )
  492. $ret = '-' . $ret;
  493. return $ret;
  494. }
  495. //NOTE: This is not used anymore is it...?
  496. class ODBCSource extends DBSourceBase {
  497. var $name;
  498. var $user;
  499. var $pass;
  500. public function __construct( $aname, $auser, $apass, $astr_enc = "windows-1252" ) {
  501. $this->name = $aname;
  502. $this->user = $auser;
  503. $this->pass = $apass;
  504. parent::__construct( $astr_enc );
  505. }
  506. function int_exec( $q ) {
  507. return odbc_exec( $this->connect, $q );
  508. }
  509. function int_errormsg( ) {
  510. return odbc_errormsg();
  511. }
  512. function int_close() {
  513. return odbc_close( $this->connect );
  514. }
  515. function int_connect() {
  516. return odbc_connect( $this->name, $this->user, $this->pass );
  517. }
  518. function &int_allresults( $res, $opts = null ) {
  519. return odbc_fetch_resultset( $res, $opts );
  520. }
  521. function int_result( $res, $col ) {
  522. return odbc_result( $res, $col );
  523. }
  524. function int_fetch_row( $res ) {
  525. return odbc_fetch_row( $res );
  526. }
  527. function escape_sql_str( $str ) {
  528. return str_replace( "'", "''", $str );
  529. }
  530. function escape_sql_blob( $data ) {
  531. die("Unsupported!");
  532. }
  533. function endresult( $res ) {
  534. }
  535. }
  536. class MySQLSource extends DBSourceBase {
  537. var $server;
  538. var $name;
  539. var $user;
  540. var $pass;
  541. var $curRow;
  542. var $curNames;
  543. var $mapRes; //maps ids to results
  544. var $refCount = 1;
  545. /* Queries will be scanned syntax %TABLE[tablename] and be translated
  546. * into just tablename. This allows the translation of table names in
  547. * queries while maintaining a readable syntax. The sytnax is additionally
  548. * chosen to prevent the string from accidentally occuring anywhere
  549. * else in a query. (It can't exist in SQL raw and the escape_str function
  550. * *should* escape such a sequence -- TODO: it doesn't now, %'s are ignored...)).
  551. */
  552. var $ucaseTables = false;
  553. public $enableCharsetNames = 'utf8'; //use to set the Names communication value (false is to use default is used)
  554. function __construct( $aserver, $aname, $auser, $apass, $astr_enc = "windows-1252" ) {
  555. $this->server = $aserver;
  556. $this->name = $aname;
  557. $this->user = $auser;
  558. $this->pass = $apass;
  559. $this->curRow = array();
  560. $this->curNames = array();
  561. $this->mapRes = array();
  562. parent::__construct( $astr_enc );
  563. }
  564. function int_exec( $q ) {
  565. $res = mysql_query( $this->translate_query( $q ), $this->connect );
  566. if( !$res )
  567. return false;//so a direct comparison works.
  568. $ref = $this->refCount++;
  569. $this->mapRes[ $ref] = $res;
  570. $this->curRow[ $ref ] = null;
  571. $this->curNames[ $ref ] = null;
  572. return $ref;
  573. }
  574. function int_errormsg( ) {
  575. return mysql_error();
  576. }
  577. function int_close() {
  578. return mysql_close( $this->connect );
  579. }
  580. function int_connect() {
  581. $result = mysql_connect( $this->server, $this->user, $this->pass, true ) or die( "<p class='error'>MySql Connect: $this->server/$this->name: " . mysql_error() );
  582. mysql_select_db( $this->name, $result ) or die( "<p class='error'>MySql Select: $this->name: " . mysql_error() );
  583. if( $this->enableCharsetNames !== false )
  584. mysql_query( "SET NAMES {$this->enableCharsetNames}", $result);
  585. return $result;
  586. }
  587. function &int_allresults( $ref, $opts = null ) {
  588. return mysql_fetch_resultset( $this->mapRes[ $ref ], $opts );
  589. }
  590. function int_result( $ref, $col ) {
  591. $res = $this->mapRes[ $ref ];
  592. if( is_null( $this->curRow[ $ref ] ) )
  593. $this->curRow[ $ref ] = $this->_get_row( $ref );
  594. $row =& $this->curRow[ $ref ];
  595. if( is_int( $col ) )
  596. return $row[ $col - 1 ]; //normal DB 1-based, mysql is 0-based
  597. else {
  598. $names =& $this->curNames[ $ref ];
  599. if( is_null( $names ) ) {
  600. $names = array();
  601. $fCount = mysql_num_fields( $res );
  602. for( $i=0; $i < $fCount; $i++)
  603. $names[ strtoupper( mysql_field_name( $res, $i ) ) ] = $i;
  604. $this->curNames[ $ref ] =& $names;
  605. }
  606. return $row[ $names[ strtoupper( $col ) ] ];
  607. }
  608. }
  609. function int_fetch_row( $ref ) {
  610. $res = $this->_get_row( $ref );
  611. if( !$res )
  612. return false;
  613. return true;
  614. }
  615. function _get_row( $ref ) {
  616. $res = $this->mapRes[ $ref ];
  617. $row = mysql_fetch_row( $res );
  618. $this->curRow[ $ref ] = $row;
  619. return $row;
  620. }
  621. function escape_sql_str( $str ) {
  622. $this->init(); //we need a connection
  623. return mysql_real_escape_string( $str, $this->connect );
  624. }
  625. function escape_sql_blob( $data ) {
  626. $this->init(); //we need a connection
  627. return mysql_real_escape_string( $data, $this->connect );
  628. }
  629. public function getNewID( $table = null, $field = null ) {
  630. return mysql_insert_id( $this->connect );
  631. }
  632. function escapeColumn( $column ) {
  633. $parts = explode( '.', $column );
  634. foreach( $parts as $key => $col )
  635. $parts[$key] = '`' . $this->escape_sql_str( $col ) . '`';
  636. return implode('.',$parts);
  637. }
  638. function translate_query( $sql ) {
  639. if( $this->ucaseTables ) {
  640. $reg = '@\%TABLE\[([^\]]*)\]@';
  641. $ret = preg_replace_callback(
  642. $reg,
  643. create_function( '$matches',
  644. 'return strtoupper($matches[1]);'
  645. ),
  646. $sql
  647. );
  648. return $ret;
  649. } else {
  650. return $sql;
  651. }
  652. }
  653. function endresult( $ref ) {
  654. //PHP: for some queries the query API returns a boolean instead of an object...
  655. $res = $this->mapRes[ $ref ];
  656. if( !is_bool( $res ) )
  657. mysql_free_result( $res );
  658. unset( $this->mapRes[$ref] );
  659. unset( $this->curRow[$ref] ); //clean up row data as well
  660. unset( $this->curNames[$ref] );
  661. }
  662. }
  663. class PGSQLSource extends DBSourceBase {
  664. var $server;
  665. var $name;
  666. var $user;
  667. var $pass;
  668. var $curRow;
  669. var $curNames; //TODO: use ref System as in MySql (otherwise memory problem?)
  670. //The mapRes system is also used in MySQL, perhaps it should be abstracted..
  671. var $mapRes; //maps ids to results
  672. function __construct( $aserver, $aname, $auser, $apass, $astr_enc = "windows-1252" ) {
  673. $this->server = $aserver;
  674. $this->name = $aname;
  675. $this->user = $auser;
  676. $this->pass = $apass;
  677. $this->curRow = array();
  678. $this->curNames = array();
  679. $this->mapRes = array();
  680. array_push( $this->mapRes, "Junk" ); //to ensure id=0 is never used
  681. parent::__construct( $astr_enc );
  682. }
  683. function int_exec( $q ) {
  684. $res = pg_query( $this->connect, $q );
  685. $ref = array_push( $this->mapRes, $res ) - 1;
  686. $this->curRow[ $ref ] = null;
  687. $this->curNames[ $ref ] = null;
  688. if( !$res )
  689. return false;
  690. return $ref;
  691. }
  692. function int_errormsg( ) {
  693. return pg_last_error( $this->connect );
  694. }
  695. function int_close() {
  696. return pg_close( $this->connect );
  697. }
  698. function int_connect() {
  699. $connstr = "host='$this->server' dbname='$this->name' user='$this->user' password='$this->pass'";
  700. $result = pg_connect( $connstr );
  701. return $result;
  702. }
  703. function &int_allresults( $res, $opts = null ) {
  704. die("Unsupported!");
  705. }
  706. function int_result( $ref, $col ) {
  707. $res = $this->mapRes[$ref];
  708. if( is_null( $this->curRow[ $ref ] ) )
  709. $this->curRow[ $ref ] = $this->_get_row( $ref );
  710. $row =& $this->curRow[ $ref ];
  711. if( is_int( $col ) )
  712. return $row[ $col - 1 ]; //normal DB 1-based, postgres is 0-based
  713. else {
  714. $names =& $this->curNames[ $ref ];
  715. if( is_null( $names ) ) {
  716. $names = array();
  717. $fCount = pg_num_fields( $res );
  718. for( $i=0; $i < $fCount; $i++)
  719. $names[ strtoupper( pg_field_name( $res, $i ) ) ] = $i;
  720. $this->curNames[ $ref ] =& $names;
  721. }
  722. return $row[ $names[ strtoupper( $col ) ] ];
  723. }
  724. }
  725. function int_fetch_row( $ref ) {
  726. $res = $this->_get_row( $ref );
  727. if( !$res )
  728. return false;
  729. return true;
  730. }
  731. function _get_row( $ref ) {
  732. $res = $this->mapRes[ $ref ];
  733. $row = pg_fetch_row( $res );
  734. $this->curRow[ $ref ] = $row;
  735. return $row;
  736. }
  737. function escape_sql_str( $str ) {
  738. //TODO: does this actually escape special characters as well?
  739. $this->init(); //we need a connection
  740. return pg_escape_string( $this->connect, $str );
  741. }
  742. function escape_sql_blob( $data ) {
  743. $this->init(); //we need a connection
  744. return pgl_escape_bytea( $this->connect, $data );
  745. }
  746. //TODO: this is copied from MySQL, does it work here too?
  747. function escapeColumn( $column ) {
  748. $parts = explode( '.', $column );
  749. foreach( $parts as $key => $col )
  750. $parts[$key] = '`' . $this->escape_sql_str( $col ) . '`';
  751. return implode('.',$parts);
  752. }
  753. function endresult( $ref ) {
  754. pg_free_result( $this->mapRes[$ref] );
  755. unset( $this->curNames[$ref] );
  756. unset( $this->curRow[$ref] );
  757. }
  758. }
  759. class OCISQLSource extends DBSourceBase {
  760. var $server;
  761. var $name;
  762. var $user;
  763. var $pass;
  764. var $curRow;
  765. var $curNames;
  766. function __construct( $aserver, $aname, $auser, $apass, $astr_enc = "iso-8859-1" ) {
  767. $this->server = $aserver;
  768. $this->name = $aname;
  769. $this->user = $auser;
  770. $this->pass = $apass;
  771. $this->curRow = array();
  772. $this->curNames = array();
  773. parent::__construct( $astr_enc );
  774. }
  775. function int_exec( $q ) {
  776. $sm = oci_parse( $this->connect, $q );
  777. $res = oci_execute( $sm );
  778. $this->curRow[ $sm ] = null;
  779. $this->curNames[ $sm ] = null;
  780. return $sm; //unlike other dbs we return the result of parse, and not execute
  781. }
  782. function int_errormsg( ) {
  783. return oci_error( $this->connect );
  784. }
  785. function int_close() {
  786. return oci_close( $this->connect );
  787. }
  788. function int_connect() {
  789. $result = oci_connect( $this->user, $this->pass, "//$this->server/$this->name" ); //TODO: use str_enc here?
  790. return $result;
  791. }
  792. function &int_allresults( $res, $opts = null ) {
  793. die("Unsupported!");
  794. }
  795. function int_result( $res, $col ) {
  796. if( is_null( $this->curRow[ $res ] ) )
  797. $this->curRow[ $res ] = $this->int_fetch_row( $res );
  798. $row =& $this->curRow[ $res ];
  799. if( is_int( $col ) )
  800. return $row[ $col ]; //oracle DB 1-based
  801. else {
  802. $names =& $this->curNames[ $res ];
  803. if( is_null( $names ) ) {
  804. $names = array();
  805. $fCount = oci_num_fields( $res );
  806. for( $i=0; $i < $fCount; $i++)
  807. $names[ strtoupper( oci_field_name( $res, $i+1 ) ) ] = $i;
  808. $this->curNames[ $res ] =& $names;
  809. }
  810. return $row[ $names[ strtoupper( $col ) ] ];
  811. }
  812. }
  813. function int_fetch_row( $res ) {
  814. $row = oci_fetch_row( $res );
  815. $this->curRow[ $res ] = $row;
  816. return $row;
  817. }
  818. function escape_sql_str( $str ) {
  819. //TODO: does this actually escape special characters as well?
  820. $this->init(); //we need a connection
  821. return pg_escape_string( $this->connect, $str );
  822. }
  823. function escape_sql_blob( $data ) {
  824. $this->init(); //we need a connection
  825. return pgl_escape_bytea( $this->connect, $data );
  826. }
  827. //TODO: this is copied from MySQL, does it work here too?
  828. function escapeColumn( $column ) {
  829. $parts = explode( '.', $column );
  830. foreach( $parts as $key => $col )
  831. $parts[$key] = '`' . $this->escape_sql_str( $col ) . '`';
  832. return implode('.',$parts);
  833. }
  834. function endresult( $res ) {
  835. oci_free_statement( $res );
  836. unset( $this->curNames[$ref] );
  837. unset( $this->curRow[$ref] );
  838. }
  839. }
  840. /**
  841. * Return all the rows returned by a query in an array.
  842. * Array index will be the column name (lowercase -- though
  843. * fieldName will be preserved as is)
  844. *
  845. * Note: Resulting set will have each row as an item plus
  846. * one additional row for the names. So the number of returned
  847. * DB results is also count(result) - 1
  848. *
  849. * @param opts [in] map of options, default is false if not set
  850. * 'row_name' => name of row to use as index for returned array
  851. * 'keep_case' => maintain the case of the fields, otherwise all lowercase
  852. *
  853. * @result [out] is an array of all rows in the resultSet
  854. * n = 1 ... number of rows returned
  855. *
  856. * if( row_name )
  857. * [column row_name of n] => rowdata
  858. * else
  859. * [n] => rowdata
  860. *
  861. * rowdata = array( column_name_X => column_value_X )
  862. */
  863. function &odbc_fetch_resultset($resID, $opts = null )
  864. {
  865. $resultSet = array();
  866. $rowName = get_option( 'row_name', $opts, null );
  867. $keep_case = get_option( 'keep_case', $opts, false );
  868. //get the names
  869. $fNames = array();
  870. $fCount = odbc_num_fields($resID);
  871. for ($i=1; $i <= $fCount; $i++) {
  872. $fNames[$i] = odbc_field_name($resID, $i);
  873. }
  874. for ($i=1; odbc_fetch_row($resID,$i); $i++) {
  875. unset( $record );
  876. $record = array();
  877. for ($j = 1; $j <= $fCount; $j++) {
  878. $fName = $keep_case
  879. ? odbc_field_name($resID, $j)
  880. : strtolower( odbc_field_name($resID, $j) );
  881. $record[$fName] = odbc_result($resID, $j);
  882. }
  883. if( !is_null( $rowName ) ) {
  884. $index =& $record[$rowName];
  885. $resultSet[ $index ] =& $record;
  886. } else
  887. $resultSet[$i] =& $record;
  888. }
  889. return $resultSet;
  890. }
  891. function &mysql_fetch_resultset($resID, $opts = null )
  892. {
  893. $resultSet=array();
  894. $rowName = get_option( 'row_name', $opts, null );
  895. $keep_case = get_option( 'keep_case', $opts, false );
  896. //get the names
  897. $fNames = array();
  898. $fCount = mysql_num_fields($resID);
  899. for ($i=0; $i < $fCount; $i++) {
  900. $fNames[$i] = mysql_field_name($resID, $i);
  901. }
  902. $i = 0;
  903. while( $row = mysql_fetch_row( $resID ) ) {
  904. unset( $record );
  905. $record = array();
  906. for ($j = 0; $j < $fCount; $j++) {
  907. $fName = $keep_case
  908. ? $fNames[ $j ]
  909. : strtolower( $fNames[ $j ] );
  910. $record[$fName] = $row[ $j ];
  911. }
  912. if( !is_null( $rowName ) ) {
  913. $index = $record[$rowName];
  914. $resultSet[ $index ] =& $record;
  915. } else
  916. $resultSet[ $i + 1 ] =& $record;
  917. $i++;
  918. }
  919. return $resultSet;
  920. }
  921. /**
  922. * Abstracts the use of result data, in general making it easier to
  923. * work with results.
  924. */
  925. class DBResult {
  926. var $result; //result from the DB
  927. var $db; //db result came from
  928. function DBResult( &$db, $result ) {
  929. $this->result = $result;
  930. $this->db =& $db;
  931. }
  932. /**
  933. * Returns a raw result
  934. */
  935. function result( $name ) {
  936. return $this->db->getResult( $this->result, $name );
  937. }
  938. function decode( $name, $type ) {
  939. return $this->db->decode( $this->result( $name ), $type );
  940. }
  941. function endResult() {
  942. $ret = $this->db->endResult( $this->result );
  943. unset( $this->result );
  944. return $ret;
  945. }
  946. /**
  947. * Returns the string value of the given column name/index
  948. */
  949. function string( $name ) {
  950. $raw = $this->db->getResult( $this->result, $name );
  951. if( is_null( $raw ) )
  952. return null;
  953. return $this->db->decode( $raw, 'string' );
  954. }
  955. /**
  956. * Returns an integer result
  957. */
  958. function integer( $name ) {
  959. $raw = $this->db->getResult( $this->result, $name );
  960. if( is_null( $raw ) )
  961. return null;
  962. return intval( $raw );
  963. }
  964. /**
  965. * Returns a float result
  966. */
  967. function float( $name ) {
  968. $raw = $this->db->getResult( $this->result, $name );
  969. if( is_null( $raw ) )
  970. return null;
  971. return floatval( $raw );
  972. }
  973. /**
  974. * Returns a PHP timestamp for the date
  975. */
  976. function date( $name ) {
  977. $raw = $this->db->getResult( $this->result, $name );
  978. if( is_null( $raw ) )
  979. return null;
  980. //remove trailing milliseconds if there (quick and dirty... *sigh* new PHP should parse them)
  981. $dot = strrpos( $raw, '.' );
  982. if( $dot !== false )
  983. $raw = substr( $raw, 0, $dot );
  984. return strtotime( $raw );
  985. }
  986. /**
  987. * Returns a PHP timestamp for the date
  988. */
  989. function datetime( $name ) {
  990. return $this->date( $name );
  991. }
  992. /**
  993. * Returns a PHP timestampe from a retrieved timestamp
  994. */
  995. function timestamp( $name ) {
  996. $str = $this->db->getResult( $this->result, $name );
  997. if( is_null( $str ) )
  998. return null;
  999. //TODO: This might be MySQL specific
  1000. $year = substr($str,0,4);
  1001. $mon = substr($str,4,2);
  1002. $day = substr($str,6,2);
  1003. $hour = substr($str,8,2);
  1004. $min = substr($str,10,2);
  1005. $sec = substr($str,12,2);
  1006. return mktime($hour,$min,$sec,$mon,$day,$year);
  1007. }
  1008. /**
  1009. * Returns a boolean value from the result
  1010. */
  1011. function boolean( $name ) {
  1012. $raw = $this->db->getResult( $this->result, $name );
  1013. if( is_null( $raw ) )
  1014. return null;
  1015. if( is_bool( $raw ) )
  1016. return $raw;
  1017. return intval( $raw ) != 0;
  1018. }
  1019. /**
  1020. * You can use this to determine if there are any results.
  1021. * if( $dbr->fetch_row() ) { ...result...}
  1022. * But you don't need to use it, the first result-set will be
  1023. * implicity retrieved on a first call to a previous function
  1024. */
  1025. function fetch_row() {
  1026. return $this->db->fetchRow( $this->result );
  1027. }
  1028. /**
  1029. * Used by data_formatter to indicate the "getter" of the
  1030. * current row. For our implementation this is just
  1031. * ourselves.
  1032. */
  1033. function obtain_getter() {
  1034. return $this;
  1035. }
  1036. }
  1037. ?>