Newer
Older
Digital_Repository / Repositories / statistics / includes / inc.class.mysql.es.php
  1. <?php
  2. /**
  3. * Project: ePrintstats
  4. * File: inc.class.mysql.es.php
  5. * Description: All SQL interactions via this object.
  6. * This includes connections, adding, updating, inserting.
  7. */
  8.  
  9. class sqlconn {
  10. var $_mysql_error = array();
  11. var $_database;
  12. var $_host_info;
  13. var $_debug = FALSE;
  14. var $_debugAll = array();
  15.  
  16. function doConnectServer($sqlserver,$sqluser,$sqlpass)
  17. {
  18. $connect = mysql_pconnect ($sqlserver,$sqluser,$sqlpass)
  19. or sqlconn::_setError(mysql_errno(),mysql_error());
  20. $db_list = mysql_list_dbs($connect);
  21. while ($row = mysql_fetch_object($db_list)) {
  22. $this->_db_list[] = $row->Database;
  23. }
  24. $this->_host_info = sprintf ("MySQL host info: %s\n", mysql_get_host_info());
  25. return $connect;
  26. }
  27. function doConnectDb($sqldatabase)
  28. {
  29. $db = mysql_select_db($sqldatabase)
  30. or sqlconn::_setError(mysql_errno(),mysql_error());
  31. sqlconn::_setError(mysql_errno(),mysql_error());
  32. $this->_database = $sqldatabase;
  33. return $db;
  34. }
  35. function closeConnection()
  36. {
  37. mysql_close();
  38. }
  39.  
  40. function getHostInfo()
  41. {
  42. return $this->_host_info;
  43. }
  44. function setDebug($debuglevel)
  45. {
  46. $this->_debug = $debuglevel;
  47. }
  48. function getDebug()
  49. {
  50. $_outstring = '';
  51. foreach ($this->_debugAll as $k=>$v) {
  52. $_outstring .= $v;
  53. }
  54. return $_outstring;
  55. }
  56. function getError()
  57. {
  58. return $this->_mysql_error;
  59. }
  60. /*
  61. * @return void
  62. * @param $input unknown
  63. * @desc HTML output debug info
  64. * @usage sqlconn::_debug($this->_db_schema);
  65. */
  66. function _debug($output,$line,$file,$function,$class)
  67. {
  68. ob_start();
  69. print "<div class=\"debug\">\n";
  70. print "<pre>\n";
  71. if (!empty($line)) { print "LINE: $line\n"; };
  72. if (!empty($file)) { print "FILE: $file\n"; };
  73. if (!empty($function)) { print "FUNCTION: $function\n"; };
  74. if (!empty($class)) { print "CLASS: $class\n"; };
  75. //if (!empty($method)) { print "METHOD: $method\n"; }; // Only > PHP5
  76. print_r($output);
  77. print "</ pre></div>";
  78. $ret_str = ob_get_contents();
  79. ob_end_clean();
  80. $this->_debugAll[] = $ret_str;
  81. }
  82. function _setError($mysql_errno,$mysql_error)
  83. {
  84. /*
  85. * May be of limited use where a method makes multiple calls
  86. * and one error message is replaced by another.
  87. */
  88. $this->_mysql_error["code"] = $mysql_errno;
  89. $this->_mysql_error["text"] = $mysql_error;
  90. }
  91.  
  92. /********************************************
  93. *
  94. *
  95. * Program specific
  96. *
  97. **********************************************/
  98.  
  99. /* ------------------------------------------------------------------------------
  100. This function is a rewritten version of getCumulativeUsage, to optionally
  101. select the view_type.
  102. (c) Copyright 2004 Arthur Sale, University of Tasmania
  103. Parameters:
  104. id # of eprint
  105. strings 'abstract', 'download' or anything else acts as view=abstract+download
  106. Returns:
  107. Result array from query or failure
  108. ------------------------------------------------------------------------------ */
  109. function getCumulativeUsageType($archive,$id,$type)
  110. {
  111. // NJS 2006-06-14: Added archive name as argument.
  112. // Build up the components of the WHERE clause (refactored).
  113. $where_parts = array();
  114. $limit = '';
  115. // Formulate the archive name part of the select.
  116. $this->addArchiveNameCondition( $archive, $where_parts );
  117. // Formulate the id part of the select.
  118. $this->addArchiveIDCondition( $id, $where_parts );
  119. // Formulate the view part of the select.
  120. $this->addViewTypeCondition( $type, $where_parts );
  121. $limit = $this->buildWhereClause( $where_parts, 'AND' );
  122. // Create a result array
  123. $records = array();
  124. //Build up the query
  125. // NJS 2006-01-18: Added count of country_code.
  126. $query = "
  127. SELECT COUNT(*) AS count,
  128. COUNT(DISTINCT country_code) AS countries,
  129. MONTH(request_date) AS monthnum,
  130. DATE_FORMAT(request_date, '%b') AS month,
  131. YEAR(request_date) AS year
  132. FROM view
  133. $limit
  134. GROUP BY year, monthnum
  135. ORDER BY year DESC, monthnum DESC
  136. ";
  137. // Make the query
  138. $result = mysql_query($query);
  139. if (!$result) {
  140. sqlconn::_setError(mysql_errno(),mysql_error());
  141. return mysql_error();
  142. } else {
  143. while ($row = mysql_fetch_assoc($result)) {
  144. $records[] = $row;
  145. }
  146. return $records;
  147. }
  148. }
  149.  
  150. /* ------------------------------------------------------------------------------
  151. This function is a rewritten version of getCumulativeUsageCountry, to optionally
  152. select the view_type.
  153. (c) Copyright 2004 Arthur Sale, University of Tasmania
  154. Parameters:
  155. id # of eprint
  156. strings 'abstract', 'download' or anything else acts as view=abstract+download
  157. Returns:
  158. Result array from query or failure
  159. ------------------------------------------------------------------------------ */
  160. function getCumulativeUsageCountryType($archive,$type)
  161. {
  162. // NJS 2006-06-14: Added archive name as argument.
  163. // Build up the components of the WHERE clause (refactored).
  164. $where_parts = array();
  165. $limit = '';
  166. // Formulate the archive name part of the select.
  167. $this->addArchiveNameCondition( $archive, $where_parts );
  168. // Formulate the view part of the select.
  169. $this->addViewTypeCondition( $type, $where_parts );
  170. $limit = $this->buildWhereClause( $where_parts, 'AND' );
  171.  
  172. // Create a result array
  173. $records = array();
  174. //Build up the query
  175. // NJS 2006-05-02: Fixed typo in ORDER BY and added ascending order of country
  176. // name for countries with the same number of accesses.
  177. $query = "
  178. SELECT COUNT(*) AS count, country_name, country_code
  179. FROM view
  180. $limit
  181. GROUP BY country_name
  182. ORDER BY count DESC, country_name ASC
  183. ";
  184. $result = mysql_query($query);
  185. if (!$result) {
  186. sqlconn::_setError(mysql_errno(),mysql_error());
  187. return mysql_error();
  188. } else {
  189. while ($row = mysql_fetch_assoc($result)) {
  190. $records[] = $row;
  191. }
  192. return $records;
  193. }
  194. }
  195. /* ------------------------------------------------------------------------------ */
  196.  
  197. function getArchiveCountDate($archive,$year,$month,$range,$type='download')
  198. {
  199. // NJS 2006-06-14: Added archive name as argument.
  200. $year = (int) $year;
  201. $month = (int) $month;
  202. $records = array();
  203. // Build up the components of the WHERE clause (refactored).
  204. $where_parts = array();
  205. $limit = '';
  206. // Formulate the archive name part of the select.
  207. $this->addArchiveNameCondition( $archive, $where_parts );
  208. // create a date based limit
  209. $this->addYearCondition( $year, $where_parts );
  210. $this->addMonthCondition( $month, $where_parts );
  211. // Note that year(+month) is mutually exclusive of "4w" (last four
  212. // weeks), but we don't check for this. (The original code didn't
  213. // check either.)
  214. $this->addFourWeeksCondition( $range, $where_parts );
  215.  
  216. // Constrain the view to downloads - Arthur Sale addition -----------
  217. $this->addViewTypeCondition( 'download', $where_parts );
  218. // End of addition --------------------------------------------------
  219. $limit = $this->buildWhereClause( $where_parts, 'AND' );
  220.  
  221. // NJS 2006-01-18: Added count of country_code.
  222. // NJS 2006-05-02: Added descending order of number of countries and
  223. // ascending order of title for eprints with the same number of
  224. // downloads.
  225. $query = "
  226. SELECT COUNT(*) AS count,
  227. COUNT(DISTINCT country_code) AS countries,
  228. archiveid,
  229. archive_name,
  230. eprint_name
  231. FROM view
  232. $limit
  233. GROUP BY archiveid, archive_name
  234. ORDER BY count DESC, countries DESC, eprint_name ASC
  235. ";
  236. $result = mysql_query($query);
  237. if (!$result) {
  238. sqlconn::_setError(mysql_errno(),mysql_error());
  239. return mysql_error();
  240. } else {
  241. while ($row = mysql_fetch_assoc($result)) {
  242. $records[] = $row;
  243. }
  244. return $records;
  245. }
  246. }
  247.  
  248. /*
  249. NJS 2005-12-15
  250. getArchiveCountCountry()
  251. arguments:
  252. $archive archive name
  253. $ccode country code
  254. $type eprint view type (e.g., download)
  255. returns: array
  256. Similar to getArchiveCountDate but does it by country code instead
  257. of by date.
  258. */
  259. function getArchiveCountCountry($archive,$ccode,$type='download')
  260. {
  261. // NJS 2006-06-14: Added archive name as argument.
  262. $records = array();
  263. // Build up the components of the WHERE clause (refactored).
  264. $where_parts = array();
  265. $limit = '';
  266. // Create a country based limit. Also constrain to downloads only
  267. // in the appropriate archive.
  268. $this->addArchiveNameCondition( $archive, $where_parts );
  269. $this->addStringCondition( 'country_code', '=', $ccode, $where_parts );
  270. $this->addViewTypeCondition( 'download', $where_parts );
  271. $limit = $this->buildWhereClause( $where_parts, 'AND' );
  272. // NJS 2003-05-02: Added ascending order of title for eprints with
  273. // the same number of downloads.
  274. $query = "
  275. SELECT COUNT(*) AS count,
  276. archiveid,
  277. archive_name,
  278. eprint_name,
  279. country_name
  280. FROM view
  281. $limit
  282. GROUP BY archiveid, archive_name
  283. ORDER BY count DESC, eprint_name ASC
  284. ";
  285. $result = mysql_query($query);
  286. if (!$result) {
  287. sqlconn::_setError(mysql_errno(),mysql_error());
  288. return mysql_error();
  289. } else {
  290. while ($row = mysql_fetch_assoc($result)) {
  291. $records[] = $row;
  292. }
  293. return $records;
  294. }
  295. }
  296.  
  297. /* New */
  298. function getCountryEprintType($archive,$year,$month,$rnge,$type,$id)
  299. {
  300. // NJS 2006-06-14: Added archive name as argument.
  301. $year = (int) $year;
  302. $month = (int) $month;
  303. $records = array();
  304.  
  305. // Build up the components of the WHERE clause (refactored).
  306. $where_parts = array();
  307. $limit = '';
  308.  
  309. $this->addArchiveNameCondition( $archive, $where_parts );
  310. $this->addArchiveIDCondition( $id, $where_parts );
  311. // Generate type enquiry
  312. $this->addViewTypeCondition( $type, $where_parts );
  313.  
  314. // generate date enquiry
  315. $this->addYearCondition( $year, $where_parts );
  316. $this->addMonthCondition( $month, $where_parts );
  317. // Note that year(+month) is mutually exclusive of "4w" (last four
  318. // weeks), but we don't check for this. (The original code didn't
  319. // check either.)
  320. $this->addFourWeeksCondition( $rnge, $where_parts );
  321.  
  322. $limit = $this->buildWhereClause( $where_parts, 'AND' );
  323. // NJS 2006-05-02: Added ascending order of country
  324. // name for countries with the same number of accesses.
  325. $query = "
  326. SELECT COUNT(*) AS count, country_name, country_code
  327. FROM view
  328. $limit
  329. GROUP BY country_name
  330. ORDER BY count DESC, country_name ASC
  331. ";
  332. $result = mysql_query($query);
  333. if (!$result) {
  334. sqlconn::_setError(mysql_errno(),mysql_error());
  335. return mysql_error();
  336. } else {
  337. while ($row = mysql_fetch_assoc($result)) {
  338. $records[] = $row;
  339. }
  340. return $records;
  341. }
  342. }
  343. function getTitle($archive,$id)
  344. {
  345. // Get the title of an eprint given its id
  346. // NJS 2006-06-14: Added archive name as argument.
  347.  
  348. // Build up the components of the WHERE clause (refactored).
  349. $where_parts = array();
  350. $limit = '';
  351.  
  352. $this->addArchiveNameCondition( $archive, $where_parts );
  353. $this->addArchiveIDCondition( $id, $where_parts );
  354.  
  355. $limit = $this->buildWhereClause( $where_parts, 'AND' );
  356.  
  357. $query = "
  358. SELECT eprint_name
  359. FROM view
  360. $limit
  361. ";
  362. $result = mysql_query($query);
  363. if (!$result) {
  364. sqlconn::_setError(mysql_errno(),mysql_error());
  365. return;
  366. } else {
  367. $row = mysql_fetch_assoc($result);
  368. return $row["eprint_name"];
  369. }
  370. }
  371. function getAbstractDownload($archive,$year,$month,$range,$type='download',$id)
  372. {
  373. // NJS 2006-06-14: Added archive name as argument.
  374.  
  375. $year = (int) $year;
  376. $month = (int) $month;
  377. $records = array();
  378.  
  379. // Build up the components of the WHERE clause (refactored).
  380. $where_parts = array();
  381. $limit = '';
  382.  
  383. $this->addArchiveNameCondition( $archive, $where_parts );
  384. $this->addArchiveIDCondition( $id, $where_parts );
  385.  
  386. $this->addYearCondition( $year, $where_parts );
  387. $this->addMonthCondition( $month, $where_parts );
  388. // Note that year(+month) is mutually exclusive of "4w" (last four
  389. // weeks), but we don't check for this. (The original code didn't
  390. // check either.)
  391. $this->addFourWeeksCondition( $range, $where_parts );
  392.  
  393. $limit = $this->buildWhereClause( $where_parts, 'AND' );
  394.  
  395. // NJS 2006-01-18: Added count of country_code.
  396. // NJS 2006-05-02: Added ascending order of country code.
  397. $query = "
  398. SELECT COUNT(*) AS count,
  399. COUNT(DISTINCT country_code) AS countries,
  400. view_type
  401. FROM view
  402. $limit
  403. GROUP BY view_type
  404. ORDER BY count DESC, country_code ASC
  405. ";
  406. $result = mysql_query($query);
  407. if (!$result) {
  408. sqlconn::_setError(mysql_errno(),mysql_error());
  409. return mysql_error();
  410. } else {
  411. while ($row = mysql_fetch_assoc($result)) {
  412. $records[] = $row;
  413. }
  414. return $records;
  415. }
  416. }
  417.  
  418. function getLastProc()
  419. {
  420. // Get the time that the stats were last processed.
  421. // Note that the archive name is irrelevant for this,
  422. // as the stats are updated for ALL archives at the
  423. // same time.
  424. //
  425. // NJS 2007-02-27: Changed from timeinsert to lastproc.
  426. // Missed this when fixing earlier bugs! Fortunately it
  427. // only affects display, not internal processing.
  428. $query = "
  429. SELECT lastproc
  430. FROM lastproc
  431. ORDER BY id DESC
  432. LIMIT 1
  433. ";
  434. $result = mysql_query($query);
  435. if (!$result) {
  436. sqlconn::_setError(mysql_errno(),mysql_error());
  437. return;
  438. } else {
  439. $row = mysql_fetch_assoc($result);
  440. return $row["lastproc"];
  441. }
  442. }
  443.  
  444.  
  445. /*
  446. NJS 2006-02-09 (moved here from inc.fns.show_detail_eprint.es.php)
  447. Grab the title of an eprint from the repository. Only needed if
  448. the title isn't already in the stats database, which should only
  449. occur when an eprint hasn't been downloaded yet.
  450. Do nothing if the archive name is empty or "default".
  451. We could be more clever about this and raise some kind of error
  452. if the eprint ID doesn't exist in the archive, but I don't see
  453. that it would add that much to the overall user experience :)
  454. */
  455. function getEPrintTitle($archive,$eprint_id)
  456. {
  457. // NJS 2006-06-14: Added archive name as argument to support
  458. // multi-archive statistics.
  459. $eprint_title = '';
  460. if ( ( $archive != '' ) and ( $archive != 'default' ) )
  461. {
  462. $conn_eprints = mysql_connect(
  463. $GLOBALS["config_vars"]["connections"]["eprints_archives"][$archive]["sqlserver_eprints"],
  464. $GLOBALS["config_vars"]["connections"]["eprints_archives"][$archive]["sqluser_eprints"],
  465. $GLOBALS["config_vars"]["connections"]["eprints_archives"][$archive]["sqlpass_eprints"]
  466. );
  467. $db_eprints = mysql_select_db( $archive, $conn_eprints );
  468. if (!$db_eprints) return $eprint_title;
  469. // NJS 2007-07-24: Added check for EPrints version, as the
  470. // database structure changed between versions 2 and 3.
  471. if ($GLOBALS["config_vars"]["general"]["eprints_version"] > 2)
  472. {
  473. $query_eprints = "
  474. SELECT title
  475. FROM eprint
  476. WHERE eprintid = $eprint_id
  477. AND eprint_status = 'archive'
  478. ";
  479. }
  480. else
  481. {
  482. $query_eprints = "
  483. SELECT title
  484. FROM archive
  485. WHERE eprintid = $eprint_id
  486. ";
  487. }
  488. $result_eprints = mysql_query($query_eprints,$conn_eprints);
  489. $row_eprints = mysql_fetch_assoc($result_eprints);
  490. $eprint_title = trim($row_eprints["title"]);
  491. $eprint_title = preg_replace("/\s+/"," ",$eprint_title);
  492. mysql_close($conn_eprints);
  493. }
  494. return $eprint_title;
  495. }
  496. /*
  497. NJS 2006-06-14
  498. buildWhereClause()
  499. arguments:
  500. $where_parts array of WHERE clause conditions
  501. $op boolean operator to separate conditions with
  502. (e.g., 'AND')
  503. returns: string
  504. Given an array of condition strings, build a WHERE clause,
  505. combining the conditions using the specified operator. (If you
  506. need to build something more complex, you'll have to do it
  507. manually.)
  508. */
  509. function buildWhereClause( $where_parts, $op )
  510. {
  511. $limit = '';
  512. $num_parts = count( $where_parts );
  513. if ( $num_parts > 0 )
  514. {
  515. $limit = 'WHERE ';
  516. // Append all but the last one, with the specified operator.
  517. for ( $i = 0; $i < ( $num_parts - 1 ); $i++ )
  518. {
  519. $limit .= $where_parts[$i] . " $op ";
  520. }
  521. // Append the last one with no operator.
  522. $limit .= $where_parts[$num_parts - 1];
  523. }
  524. return $limit;
  525. }
  526. /*
  527. NJS 2006-06-14
  528. addArchiveNameCondition()
  529. arguments:
  530. $archive the name of an archive
  531. $where_parts reference to an array to which to add the new
  532. condition string
  533. returns: void
  534. Add a condition for the archive name to the list of WHERE
  535. conditions. Do nothing if the archive name is empty. We also
  536. ignore the value "default" in order to be backwards-compatible
  537. with earlier versions of the stats package. Note that in either
  538. of the latter two cases this means that ALL stats for ALL
  539. archives will be queried!
  540. */
  541. function addArchiveNameCondition ( $archive, &$where_parts )
  542. {
  543. if ( ( $archive != '' ) and ( $archive != 'default' ) )
  544. $this->addStringCondition( 'archive_name', '=', $archive, $where_parts );
  545. }
  546.  
  547. /*
  548. NJS 2006-06-14
  549. addArchiveIDCondition()
  550. arguments:
  551. $id the id of an eprint
  552. $where_parts reference to an array to which to add the new
  553. condition string
  554. returns: void
  555. Add a condition for the eprint ID to the list of WHERE
  556. conditions. Do nothing if the eprint ID is zero or less.
  557. */
  558. function addArchiveIDCondition ( $id, &$where_parts )
  559. {
  560. $id = (int) $id;
  561. // BUG FIX: ID should be > 0, not > 1!
  562. if ( $id > 0 ) $this->addNumericCondition( 'archiveid', '=', $id, $where_parts );
  563. }
  564.  
  565. /*
  566. NJS 2006-06-14
  567. addViewTypeCondition()
  568. arguments:
  569. $type the type of archive view (download or abstract)
  570. $where_parts reference to an array to which to add the new
  571. condition string
  572. returns: void
  573. Add a condition for the eprint view type to the list of WHERE
  574. conditions. Do nothing if the view type is not "download" or
  575. "abstract".
  576. */
  577. function addViewTypeCondition ( $type, &$where_parts )
  578. {
  579. if ( ( $type == 'download' ) or ( $type == 'abstract' ) )
  580. $this->addStringCondition( 'view_type', '=', $type, $where_parts );
  581. }
  582.  
  583. /*
  584. NJS 2006-06-14
  585. addYearCondition()
  586. arguments:
  587. $year the year to filter on
  588. $where_parts reference to an array to which to add the new
  589. condition string
  590. returns: void
  591. Add a condition for the year to the list of WHERE conditions. Do
  592. nothing if the year is outside the range 1990--2100. (Note:
  593. lower bound changed from 2002 as it seems more appropriate.)
  594.  
  595. NJS 2006-01-18: Fixed upper year bound (was 2005).
  596. */
  597. function addYearCondition ( $year, &$where_parts )
  598. {
  599. if ( ( $year >= 1990 ) and ( $year < 2100 ) )
  600. $this->addStringCondition( 'YEAR(request_date)', '=', $year, $where_parts );
  601. }
  602.  
  603. /*
  604. NJS 2006-06-14
  605. addMonthCondition()
  606. arguments:
  607. $month the month to filter on
  608. $where_parts reference to an array to which to add the new
  609. condition string
  610. returns: void
  611. Add a condition for the month to the list of WHERE conditions.
  612. Do nothing if the month is outside the range 1--12.
  613. */
  614. function addMonthCondition ( $month, &$where_parts )
  615. {
  616. if ( ( $month > 0 ) and ( $month <= 12 ) )
  617. $this->addStringCondition( 'MONTH(request_date)', '=', $month, $where_parts );
  618. }
  619.  
  620. /*
  621. NJS 2006-06-14
  622. addFourWeeksCondition()
  623. arguments:
  624. $range the range to filter on
  625. $where_parts reference to an array to which to add the new
  626. condition string
  627. returns: void
  628. Add a condition for the last four weeks to the list of WHERE
  629. conditions. Do nothing if the range is not "4w".
  630. */
  631. function addFourWeeksCondition ( $range, &$where_parts )
  632. {
  633. if ( $range == '4w' )
  634. $this->addExprCondition( 'request_date', '>=', 'DATE_SUB(CURDATE(),INTERVAL 1 MONTH)', $where_parts );
  635. }
  636.  
  637. /*
  638. NJS 2006-06-14
  639. addStringCondition()
  640. arguments:
  641. $column the name of the SQL column
  642. $op the comparison operator
  643. $value the value to be tested
  644. $where_parts reference to an array to which to add the new
  645. condition string
  646. returns: void
  647. Add a generic condition for a string column to the list of WHERE
  648. conditions. Any validation of the value should be done prior to
  649. calling this function.
  650. */
  651. function addStringCondition ( $column, $op, $value, &$where_parts )
  652. {
  653. $where_parts[] = "$column $op '$value'";
  654. }
  655.  
  656. /*
  657. NJS 2006-06-14
  658. addNumericCondition()
  659. arguments:
  660. $column the name of the SQL column
  661. $op the comparison operator
  662. $value the value to be tested
  663. $where_parts reference to an array to which to add the new
  664. condition string
  665. returns: void
  666. Add a generic condition for a numeric column to the list of WHERE
  667. conditions. Any validation of the value should be done prior to
  668. calling this function.
  669. */
  670. function addNumericCondition ( $column, $op, $value, &$where_parts )
  671. {
  672. $where_parts[] = "$column $op $value";
  673. }
  674.  
  675. /*
  676. NJS 2006-06-16
  677. addExprCondition()
  678. arguments:
  679. $column the name of the SQL column
  680. $op the comparison operator
  681. $expr the expression to be tested
  682. $where_parts reference to an array to which to add the new
  683. condition string
  684. returns: void
  685. Add to the list of WHERE conditions a generic condition for a
  686. column to be tested against some arbitrary expression. Any
  687. validation of the value should be done prior to calling this
  688. function.
  689. */
  690. function addExprCondition ( $column, $op, $expr, &$where_parts )
  691. {
  692. $where_parts[] = "$column $op $expr";
  693. }
  694.  
  695.  
  696. }
  697.  
  698. ?>
  699.