PageRenderTime 117ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/library/database/class.sqldriver.php

https://github.com/champx/Garden
PHP | 1947 lines | 895 code | 267 blank | 785 comment | 237 complexity | debba88b58fb4cb44a5a9449676d8621 MD5 | raw file
Possible License(s): LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. <?php if (!defined('APPLICATION')) exit();
  2. /*
  3. Copyright 2008, 2009 Vanilla Forums Inc.
  4. This file is part of Garden.
  5. Garden is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
  6. Garden is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  7. You should have received a copy of the GNU General Public License along with Garden. If not, see <http://www.gnu.org/licenses/>.
  8. Contact Vanilla Forums Inc. at support [at] vanillaforums [dot] com
  9. */
  10. /**
  11. * The Gdn_DatabaseDriver class (equivalent to SqlBuilder from Vanilla 1.x) is used
  12. * by any given database driver to build and execute database queries.
  13. *
  14. * This class is HEAVILY inspired by and, in places, flat out copied from
  15. * CodeIgniter (http://www.codeigniter.com). My hat is off to them.
  16. *
  17. * @author Mark O'Sullivan
  18. * @copyright 2003 Mark O'Sullivan
  19. * @license http://www.opensource.org/licenses/gpl-2.0.php GPL
  20. * @package Garden
  21. * @version @@GARDEN-VERSION@@
  22. * @namespace Garden.Database
  23. */
  24. require_once(dirname(__FILE__).DS.'class.database.php');
  25. abstract class Gdn_SQLDriver {
  26. /// CONSTRUCTOR ///
  27. public function __construct() {
  28. $this->ClassName = get_class($this);
  29. $this->Reset();
  30. }
  31. /// PROPERTIES ///
  32. /**
  33. * An associative array of table alias => table name pairs.
  34. *
  35. * @var array
  36. */
  37. protected $_AliasMap;
  38. /**
  39. *
  40. * @var bool Whether or not to capture (not execute) DML statements.
  41. */
  42. public $CaptureModifications = FALSE;
  43. /**
  44. * The name of the class that has been instantiated.
  45. *
  46. * @var string
  47. */
  48. public $ClassName;
  49. /**
  50. * The database connection.
  51. *
  52. * @var Gdn_Database The connection and engine information for the database.
  53. */
  54. public $Database;
  55. /**
  56. * An associative array of information about the database to which the
  57. * application is connected. Values include: Engine, Version, DatabaseName.
  58. *
  59. * @var string
  60. */
  61. protected $_DatabaseInfo = array();
  62. /**
  63. * A boolean value indicating if this is a distinct query.
  64. *
  65. * @var boolean
  66. */
  67. protected $_Distinct;
  68. /**
  69. * A collection of tables from which data is being selected.
  70. *
  71. * @var array
  72. */
  73. protected $_Froms;
  74. /**
  75. * A collection of group by clauses.
  76. *
  77. * @var array
  78. */
  79. protected $_GroupBys;
  80. /**
  81. * A collection of having clauses.
  82. *
  83. * @var array
  84. */
  85. protected $_Havings;
  86. /**
  87. * A collection of tables which have been joined to.
  88. *
  89. * @var array
  90. */
  91. protected $_Joins;
  92. /**
  93. * The number of records to limit the query to. FALSE by default.
  94. *
  95. * @var int
  96. */
  97. protected $_Limit;
  98. /**
  99. * An associative array of parameter_name => parameter_value pairs to be
  100. * inserted into the prepared $this->_PDOStatement.
  101. *
  102. * @var array
  103. */
  104. protected $_NamedParameters = array();
  105. /**
  106. * Whether or not to reset the properties when a query is executed.
  107. *
  108. * @var int
  109. * 0 = The object will reset after query execution.
  110. * 1 = The object will not reset after the <b>NEXT</b> query execution.
  111. * 2 = The object will not reset after <b>ALL</b> query executions.
  112. */
  113. protected $_NoReset = FALSE;
  114. /**
  115. * The offset from which data should be returned. FALSE by default.
  116. *
  117. * @var int
  118. */
  119. protected $_Offset;
  120. /**
  121. * The number of where groups currently open.
  122. *
  123. * @var int
  124. */
  125. protected $_OpenWhereGroupCount;
  126. /**
  127. * A collection of order by statements.
  128. *
  129. * @var array
  130. */
  131. protected $_OrderBys;
  132. /**
  133. * A collection of fields that are being selected.
  134. *
  135. * @var array
  136. */
  137. protected $_Selects;
  138. /**
  139. * An associative array of Field Name => Value pairs to be saved
  140. * to the database.
  141. *
  142. * @var array
  143. */
  144. protected $_Sets;
  145. /**
  146. * The logical operator used to concatenate where clauses.
  147. *
  148. * @var string
  149. */
  150. protected $_WhereConcat;
  151. /**
  152. * The default $_WhereConcat that will be reverted back to after every where clause is appended.
  153. *
  154. * @var string
  155. */
  156. protected $_WhereConcatDefault;
  157. /**
  158. * The number of where groups to open.
  159. *
  160. * @var int
  161. */
  162. protected $_WhereGroupCount;
  163. /**
  164. * A collection of where clauses.
  165. *
  166. * @var array
  167. */
  168. protected $_Wheres;
  169. /// METHODS ///
  170. /**
  171. * Removes table aliases from an array of JOIN ($this->_Joins) and GROUP BY
  172. * ($this->_GroupBys) strings. Returns the $Statements array with prefixes
  173. * removed.
  174. *
  175. * @param array $Statements The string specification of the table. ie.
  176. * "tbl_User as u" or "user u".
  177. * @return array the array of filtered statements.
  178. */
  179. //protected function _FilterTableAliases($Statements) {
  180. // foreach ($Statements as $k => $v) {
  181. // foreach ($this->_AliasMap as $Alias => $Table) {
  182. // $Statement = preg_replace('/(\w+\.\w+)/', $this->EscapeIdentifier('$0'), $v); // Makes `table.field`
  183. // $Statement = str_replace(array($this->Database->DatabasePrefix.$Table, '.'), array($Table, $this->EscapeSql('.')), $Statement);
  184. // }
  185. // $Statements[$k] = $Statement;
  186. // }
  187. // return $Statements;
  188. //}
  189. /**
  190. * Concat the next where expression with an 'and' operator.
  191. * <b>Note</b>: Since 'and' is the default operator to begin with this method doesn't usually have to be called,
  192. * unless Gdn_DatabaseDriver::Or(FALSE) has previously been called.
  193. *
  194. * @param boolean $SetDefault Whether or not the 'and' is one time or sets the default operator.
  195. * @return Gdn_DatabaseDriver $this
  196. * @see Gdn_DatabaseDriver::OrOp()
  197. */
  198. public function AndOp($SetDefault = FALSE) {
  199. $this->_WhereConcat = 'and';
  200. if($SetDefault) {
  201. $this->_WhereConcatDefault = 'and';
  202. }
  203. return $this;
  204. }
  205. public function ApplyParameters($Sql, $Parameters) {
  206. // Sort the parameters so that we don't have clashes.
  207. krsort($Parameters);
  208. foreach ($Parameters as $Key => $Value) {
  209. if (is_null($Value))
  210. $QValue = 'null';
  211. else
  212. $QValue = $this->Database->Connection()->quote($Value);
  213. $Sql = str_replace($Key, $QValue, $Sql);
  214. }
  215. return $Sql;
  216. }
  217. /**
  218. * Begin bracketed group in the where clause to group logical expressions together.
  219. *
  220. * @return Gdn_DatabaseDriver $this
  221. */
  222. public function BeginWhereGroup() {
  223. $this->_WhereGroupCount++;
  224. $this->_OpenWhereGroupCount++;
  225. return $this;
  226. }
  227. /**
  228. * Returns a single Condition Expression for use in a 'where' or an 'on' clause.
  229. *
  230. * @param string $Field The name of the field on the left hand side of the expression.
  231. * If $Field ends with an operator, then it used for the comparison. Otherwise '=' will be used.
  232. * @param mixed $Value The value on the right side of the expression. This has different behaviour depending on the type.
  233. * <b>string</b>: The value will be used. If $EscapeValueSql is true then it will end up in a parameter.
  234. * <b>array</b>: DatabaseFunction => Value will be used. if DatabaseFunction contains a "%s" then sprintf will be used.
  235. * In this case Value will be assumed to be a string.
  236. *
  237. * <b>New Syntax</b>
  238. * The $Field and Value expressions can begin with special characters to do certain things.
  239. * <ul>
  240. * <li><b>=</b>: This means that the argument is a function call.
  241. * If you want to pass field reference arguments into the function then enclose them in square brackets.
  242. * ex. <code>'=LEFT([u.Name], 4)'</code> will call the LEFT database function on the u.Name column.</li>
  243. * <li><b>@</b>: This means that the argument is a literal.
  244. * This is useful for passing in literal numbers.</li>
  245. * <li><b>no prefix></b>: This will treat the argument differently depending on the argument.
  246. * - <b>$Field</b> - The argument is a column reference.
  247. * - <b>$Value</b> - The argument will become a named parameter.
  248. * </li></ul>
  249. * @return string The single expression.
  250. */
  251. public function ConditionExpr($Field, $Value, $EscapeFieldSql = TRUE, $EscapeValueSql = TRUE) {
  252. // Change some variables from the old parameter style to the new one.
  253. // THIS PART OF THE FUNCTION SHOULD EVENTUALLY BE REMOVED.
  254. if($EscapeFieldSql === FALSE) {
  255. $Field = '@' . $Field;
  256. }
  257. if(is_array($Value)) {
  258. $FunctionCall = array_keys($Value);
  259. $FunctionCall = $FunctionCall[0];
  260. $FunctionArg = $Value[$FunctionCall];
  261. if($EscapeValueSql)
  262. $FunctionArg = '[' . $FunctionArg . ']';
  263. if(stripos($FunctionCall, '%s') === FALSE)
  264. $Value = '=' . $FunctionCall . '(' . $FunctionArg . ')';
  265. else
  266. $Value = '=' . sprintf($FunctionCall, $FunctionArg);
  267. $EscapeValueSql = FALSE;
  268. } else if(!$EscapeValueSql && !is_null($Value)) {
  269. $Value = '@' . $Value;
  270. }
  271. // Check for a straight literal field expression.
  272. if(!$EscapeFieldSql && !$EscapeValueSql && is_null($Value))
  273. return substr($Field, 1); // warning: might not be portable across different drivers
  274. $Expr = ''; // final expression which is built up
  275. $Op = ''; // logical operator
  276. // Try and split an operator out of $Field.
  277. $FieldOpRegex = "/(?:\s*(=|<>|>|<|>=|<=)\s*$)|\s+(like|not\s+like)\s*$|\s+(?:(is)\s+(null)|(is\s+not)\s+(null))\s*$/i";
  278. $Split = preg_split($FieldOpRegex, $Field, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
  279. if(count($Split) > 1) {
  280. $Field = $Split[0];
  281. $Op = $Split[1];
  282. if (count($Split) > 2) {
  283. $Value = null;
  284. }
  285. } else {
  286. $Op = '=';
  287. }
  288. if($Op == '=' && is_null($Value)) {
  289. // This is a special case where the value SQL is checking for an is null operation.
  290. $Op = 'is';
  291. $Value = '@null';
  292. $EscapeValueSql = FALSE;
  293. }
  294. // Add the left hand side of the expression.
  295. $Expr .= $this->_ParseExpr($Field, NULL, $EscapeFieldSql);
  296. // Add the expression operator.
  297. $Expr .= ' '.$Op.' ';
  298. if ($Op == 'is' || $Op == 'is not' && is_null($Value)) {
  299. $Expr .= 'null';
  300. } else {
  301. // Add the right side of the expression.
  302. $Expr .= $this->_ParseExpr($Value, $Field, $EscapeValueSql);
  303. }
  304. return $Expr;
  305. }
  306. /**
  307. * Returns the name of the database currently connected to.
  308. */
  309. public function DatabaseName() {
  310. return $this->Information('DatabaseName');
  311. }
  312. /**
  313. * Builds and executes a delete from query.
  314. *
  315. * @param mixed $Table The table (or array of table names) to delete from.
  316. * @param mixed $Where The string on the left side of the where comparison, or an associative
  317. * array of Field => Value items to compare.
  318. * @param int $Limit The number of records to limit the query to.
  319. */
  320. public function Delete($Table = '', $Where = '', $Limit = FALSE) {
  321. if ($Table == '') {
  322. if (!isset($this->_Froms[0]))
  323. return FALSE;
  324. $Table = $this->_Froms[0];
  325. } elseif (is_array($Table)) {
  326. foreach ($Table as $t) {
  327. $this->Delete($t, $Where, $Limit, FALSE);
  328. }
  329. return;
  330. } else {
  331. $Table = $this->EscapeIdentifier($this->Database->DatabasePrefix.$Table);
  332. }
  333. if ($Where != '')
  334. $this->Where($Where);
  335. if ($Limit !== FALSE)
  336. $this->Limit($Limit);
  337. if (count($this->_Wheres) == 0)
  338. return FALSE;
  339. $Sql = $this->GetDelete($Table, $this->_Wheres, $this->_Limit);
  340. return $this->Query($Sql, 'delete');
  341. }
  342. /**
  343. * Specifies that the query should be run as a distinct so that duplicate
  344. * columns are grouped together. Returns this object for chaining purposes.
  345. *
  346. * @param boolean $Bool A boolean value indicating if the query should be distinct or not.
  347. */
  348. public function Distinct($Bool = TRUE) {
  349. $this->_Distinct = (is_bool($Bool)) ? $Bool : TRUE;
  350. return $this;
  351. }
  352. /**
  353. * Removes all data from a table.
  354. *
  355. * @param string $Table The table to empty.
  356. */
  357. public function EmptyTable($Table = '') {
  358. if ($Table == '') {
  359. if (!isset($this->_Froms[0]))
  360. return FALSE;
  361. $Table = $this->_Froms[0];
  362. } else {
  363. $Table = $this->EscapeIdentifier($this->Database->DatabasePrefix.$Table);
  364. }
  365. $Sql = $this->GetDelete($Table);
  366. return $this->Query($Sql, 'delete');
  367. }
  368. /**
  369. * Closes off any open elements in the query before execution.
  370. * Ideally, the programmer should have everything closed off so this method will do nothing.
  371. */
  372. protected function _EndQuery() {
  373. // Close the where groups.
  374. while($this->_WhereGroupCount > 0) {
  375. $this->EndWhereGroup();
  376. }
  377. }
  378. /**
  379. * End a bracketed group in the where clause.
  380. * <b>Note</b>: If no items where added to the group then no barackets will appear in the final statement.
  381. *
  382. * @return Gdn_DatabaseDriver $this.
  383. */
  384. public function EndWhereGroup() {
  385. if($this->_WhereGroupCount > 0) {
  386. $WhereCount = count($this->_Wheres);
  387. if($this->_OpenWhereGroupCount >= $this->_WhereGroupCount)
  388. $this->_OpenWhereGroupCount--;
  389. else if($WhereCount > 0)
  390. $this->_Wheres[$WhereCount-1] .= ')';
  391. $this->_WhereGroupCount--;
  392. }
  393. return $this;
  394. }
  395. /**
  396. * Takes a string formatted as an SQL field reference and escapes it for the defined database engine.
  397. *
  398. * @param string $RefExpr The reference expression to be escaped.
  399. * The reference should be in the form of alias.column.
  400. */
  401. protected function EscapeIdentifier($RefExpr) {
  402. trigger_error(ErrorMessage('The selected database engine does not perform the requested task.', $this->ClassName, 'EscapeSql'), E_USER_ERROR);
  403. }
  404. /**
  405. * Takes a string of SQL and escapes it for the defined database engine.
  406. * ie. adds backticks or any other database-specific formatting.
  407. *
  408. * @param mixed $String The string (or array of strings) of SQL to be escaped.
  409. * @param boolean $FirstWordOnly A boolean value indicating if the first word should be escaped only.
  410. */
  411. protected function EscapeSql($String, $FirstWordOnly = FALSE) {
  412. trigger_error(ErrorMessage('The selected database engine does not perform the requested task.', $this->ClassName, 'EscapeSql'), E_USER_ERROR);
  413. }
  414. /**
  415. * Returns a platform-specific query to fetch column data from $Table.
  416. *
  417. * @param string $Table The name of the table to fetch column data from.
  418. */
  419. public function FetchColumnSql($Table) {
  420. trigger_error(ErrorMessage('The selected database engine does not perform the requested task.', $this->ClassName, 'FetchColumnSql'), E_USER_ERROR);
  421. }
  422. /**
  423. * Returns a platform-specific query to fetch table names.
  424. * @param mixed $LimitToPrefix Whether or not to limit the search to tables with the database prefix or a specific table name. The following types can be given for this parameter:
  425. * - <b>TRUE</b>: The search will be limited to the database prefix.
  426. * - <b>FALSE</b>: All tables will be fetched. Default.
  427. * - <b>string</b>: The search will be limited to a like clause. The ':_' will be replaced with the database prefix.
  428. */
  429. public function FetchTableSql($LimitToPrefix = FALSE) {
  430. trigger_error(ErrorMessage('The selected database engine does not perform the requested task.', $this->ClassName, 'FetchTableSql'), E_USER_ERROR);
  431. }
  432. /**
  433. * Returns an array containing table names in the database.
  434. * @param mixed $LimitToPrefix Whether or not to limit the search to tables with the database prefix or a specific table name. The following types can be given for this parameter:
  435. * - <b>TRUE</b>: The search will be limited to the database prefix.
  436. * - <b>FALSE</b>: All tables will be fetched. Default.
  437. * - <b>string</b>: The search will be limited to a like clause. The ':_' will be replaced with the database prefix.
  438. */
  439. public function FetchTables($LimitToPrefix = FALSE) {
  440. $Sql = $this->FetchTableSql($LimitToPrefix);
  441. $Data = $this->Query($Sql);
  442. $Return = array();
  443. foreach($Data->ResultArray() as $Row) {
  444. if (isset($Row['TABLE_NAME']))
  445. $Return[] = $Row['TABLE_NAME'];
  446. else
  447. $Return[] = array_shift($Row);
  448. }
  449. return $Return;
  450. }
  451. /**
  452. * Returns an array of schema data objects for each field in the specified
  453. * table. The returned array of objects contains the following properties:
  454. * Name, PrimaryKey, Type, AllowNull, Default, Length, Enum.
  455. *
  456. * @param string $Table The name of the table to get schema data for.
  457. */
  458. public function FetchTableSchema($Table) {
  459. trigger_error(ErrorMessage('The selected database engine does not perform the requested task.', $this->ClassName, 'FetchTableSchema'), E_USER_ERROR);
  460. }
  461. /**
  462. * Returns a string of SQL that retrieves the database engine version in the
  463. * fieldname "version".
  464. */
  465. public function FetchVersionSql() {
  466. trigger_error(ErrorMessage('The selected database engine does not perform the requested task.', $this->ClassName, 'FetchVersionSql'), E_USER_ERROR);
  467. }
  468. /**
  469. * Returns an array containing column names from $Table.
  470. *
  471. * @param string $Table The name of the table to fetch column data from.
  472. */
  473. public function FetchColumns($Table) {
  474. $Sql = $this->FetchColumnSql($Table);
  475. $Data = $this->Query($Sql);
  476. $Return = array();
  477. foreach($Data->ResultArray() as $Row) {
  478. if (isset($Row['COLUMN_NAME']))
  479. $Return[] = $Row['COLUMN_NAME'];
  480. else
  481. $Return[] = current($Row);
  482. }
  483. return $Return;
  484. }
  485. /**
  486. * Takes a table name and makes sure it is formatted for this database
  487. * engine.
  488. *
  489. * @param string $Table The name of the table name to format.
  490. */
  491. public function FormatTableName($Table) {
  492. trigger_error(ErrorMessage('The selected database engine does not perform the requested task.', $this->ClassName, 'FormatTableName'), E_USER_ERROR);
  493. }
  494. /**
  495. * The table(s) from which to select values. Returns this object for
  496. * chaining purposes.
  497. *
  498. * @param mixed $From A string or array of table names/aliases from which to select data.
  499. * Accepted formats include:
  500. * user
  501. * user, user u2, role
  502. * array("user u", "user u2", "role")
  503. **/
  504. public function From($From) {
  505. if (!is_array($From))
  506. $From = array($From);
  507. $Count = count($From);
  508. $i = 0;
  509. for ($i = 0; $i < $Count; ++$i) {
  510. $this->_Froms[] = $this->EscapeIdentifier($this->MapAliases($From[$i]));
  511. }
  512. return $this;
  513. }
  514. /**
  515. * Returns a string of comma delimited table names to select from.
  516. *
  517. * @param mixed $Tables The name of a table (or an array of table names) to be added in the from
  518. * clause of a query.
  519. */
  520. protected function _FromTables($Tables) {
  521. return is_array($Tables) ? implode(', ', $Tables) : $Tables;
  522. }
  523. /**
  524. * Builds the select statement and runs the query, returning a result object.
  525. *
  526. * @param string $Table The table from which to select data. Adds to the $this->_Froms collection.
  527. * @param string $OrderFields A string of fields to be ordered.
  528. * @param string $OrderDirection The direction of the sort.
  529. * @param int $Limit Adds a limit to the query.
  530. * @param int $PageNumber The page of data to retrieve.
  531. * @return DataSet
  532. */
  533. public function Get($Table = '', $OrderFields = '', $OrderDirection = 'asc', $Limit = FALSE, $PageNumber = FALSE) {
  534. if ($Table != '') {
  535. //$this->MapAliases($Table);
  536. $this->From($Table);
  537. }
  538. if ($OrderFields != '')
  539. $this->OrderBy($OrderFields, $OrderDirection);
  540. if ($Limit !== FALSE) {
  541. if ($PageNumber == FALSE || $PageNumber < 1)
  542. $PageNumber = 1;
  543. $Offset = ($PageNumber - 1) * $Limit;
  544. $this->Limit($Limit, $Offset);
  545. }
  546. $Result = $this->Query($this->GetSelect());
  547. return $Result;
  548. }
  549. /**
  550. * A helper function for escaping sql identifiers.
  551. * @param string The sql containing identifiers to escape in a different language.
  552. * All identifiers requiring escaping should be enclosed in back ticks (`).
  553. * @return array All of the tokens in the sql. The tokens that require escaping will still have back ticks.
  554. */
  555. protected function _GetIdentifierTokens($Sql) {
  556. $Tokens = preg_split('/`/', $Sql, -1, PREG_SPLIT_DELIM_CAPTURE);
  557. $Result = array();
  558. $InIdent = FALSE;
  559. $CurrentToken = '';
  560. for($i = 0; $i < count($Tokens); $i++) {
  561. $Token = $Tokens[i];
  562. $Result .= $Token;
  563. if($Token == '`') {
  564. if($InIdent && $i < count($Tokens) - 1 && $Tokens[$i + 1] == '`') {
  565. // This is an escaped back tick.
  566. $i++; // skip next token
  567. } else if($InIdent) {
  568. $Result[] = $CurrentToken;
  569. $CurrentToken = $CurrentToken;
  570. $InIdent = false;
  571. } else {
  572. $InIdent = true;
  573. }
  574. } else if(!$InIdent) {
  575. $Result[] = $CurrentToken;
  576. $CurrentToken = '';
  577. }
  578. }
  579. return $Result;
  580. }
  581. /**
  582. * Returns the total number of records in the specified table.
  583. *
  584. * @param string $Table The table from which to count rows of data.
  585. * @param mixed $Where Adds to the $this->_Wheres collection using $this->Where();
  586. */
  587. public function GetCount($Table = '', $Where = FALSE) {
  588. if($Table != '') {
  589. //$this->MapAliases($Table);
  590. $this->From($Table);
  591. }
  592. if ($Where !== FALSE)
  593. $this->Where($Where);
  594. $this->Select('*', 'count', 'RowCount');
  595. $Sql = $this->GetSelect();
  596. $Result = $this->Query($Sql);
  597. $CountData = $Result->FirstRow();
  598. return $CountData->RowCount;
  599. }
  600. /**
  601. * Returns the total number of records in the specified table.
  602. *
  603. * @param string $Table The table from which to count rows of data.
  604. * @param mixed $Like Adds to the $this->_Wheres collection using $this->Like();
  605. */
  606. public function GetCountLike($Table = '', $Like = FALSE) {
  607. if ($Table != '') {
  608. $this->MapAliases($Table);
  609. $this->From($Table);
  610. }
  611. if ($Like !== FALSE)
  612. $this->Like($Like);
  613. $this->Select('*', 'count', 'RowCount');
  614. $Result = $this->Query($this->GetSelect());
  615. $CountData = $Result->FirstRow();
  616. return $CountData->RowCount;
  617. }
  618. /**
  619. * Returns a delete statement for the specified table and the supplied
  620. * conditions.
  621. *
  622. * @param string $TableName The name of the table to delete from.
  623. * @param array $Wheres An array of where conditions.
  624. */
  625. public function GetDelete($TableName, $Wheres = array()) {
  626. trigger_error(ErrorMessage('The selected database engine does not perform the requested task.', $this->ClassName, 'GetDelete'), E_USER_ERROR);
  627. }
  628. /**
  629. * Returns an insert statement for the specified $Table with the provided $Data.
  630. *
  631. * @param string $Table The name of the table to insert data into.
  632. * @param string $Data An associative array of FieldName => Value pairs that should be inserted
  633. * $Table.
  634. */
  635. public function GetInsert($Table, $Data) {
  636. trigger_error(ErrorMessage('The selected database engine does not perform the requested task.', $this->ClassName, 'GetInsert'), E_USER_ERROR);
  637. }
  638. /**
  639. * Adds a limit clause to the provided query for this database engine.
  640. *
  641. * @param string $Query The SQL string to which the limit statement should be appended.
  642. * @param int $Limit The number of records to limit the query to.
  643. * @param int $Offset The number of records to offset the query from.
  644. */
  645. public function GetLimit($Query, $Limit, $Offset) {
  646. trigger_error(ErrorMessage('The selected database engine does not perform the requested task.', $this->ClassName, 'GetLimit'), E_USER_ERROR);
  647. }
  648. /**
  649. * Builds the select statement based on the various collections in this
  650. * object. This method should not be called directly; it is called by
  651. * $this->Get() and $this->GetWhere().
  652. */
  653. public function GetSelect() {
  654. // Close off any open query elements.
  655. $this->_EndQuery();
  656. $Sql = (!$this->_Distinct) ? 'select ' : 'select distinct ';
  657. // Don't escape the field if it is numeric or an asterisk (all columns)
  658. $Selects = array();
  659. foreach($this->_Selects as $Key => $Expr) {
  660. $Field = $Expr['Field'];
  661. $Function = $Expr['Function'];
  662. $Alias = $Expr['Alias'];
  663. $CaseOptions = ArrayValue('CaseOptions', $Expr);
  664. if ($Field != '*' && !is_numeric($Field))
  665. $Field = $this->EscapeIdentifier($Field);
  666. if ($Alias == '' && $Function != '')
  667. $Alias = $Field;
  668. // if (in_array(strtolower($Function), array('max', 'min', 'avg', 'sum', 'count')))
  669. if ($Function != '') {
  670. if(strpos($Function, '%s') !== FALSE)
  671. $Field = sprintf($Function, $Field);
  672. else
  673. $Field = $Function.'('.$Field.')';
  674. }
  675. if ($CaseOptions !== FALSE)
  676. $Field = 'case ' . $Field . $CaseOptions . ' end';
  677. if ($Alias != '')
  678. $Field .= ' as '.$this->QuoteIdentifier($Alias);
  679. if ($Field != '')
  680. $Selects[] = $Field;
  681. }
  682. $Sql .= (count($Selects) == 0) ? '*' : implode(', ', $Selects);
  683. if (count($this->_Froms) > 0)
  684. $Sql .= "\nfrom ".$this->_FromTables($this->_Froms);
  685. if (count($this->_Joins) > 0) {
  686. $Sql .= "\n";
  687. $Sql .= implode("\n", $this->_Joins);
  688. }
  689. if (count($this->_Wheres) > 0)
  690. $Sql .= "\nwhere ";
  691. $Sql .= implode("\n", $this->_Wheres);
  692. // Close any where groups that were left open.
  693. for ($i = 0; $i < $this->_OpenWhereGroupCount; ++$i) {
  694. $Sql .= ')';
  695. }
  696. $this->_OpenWhereGroupCount = 0;
  697. if (count($this->_GroupBys) > 0) {
  698. $Sql .= "\ngroup by ";
  699. // special consideration for table aliases
  700. if (count($this->_AliasMap) > 0 && $this->Database->DatabasePrefix)
  701. $Sql .= implode(', ', $this->_FilterTableAliases($this->_GroupBys));
  702. else
  703. $Sql .= implode(', ', $this->_GroupBys);
  704. }
  705. if (count($this->_Havings) > 0)
  706. $Sql .= "\nhaving ".implode("\n", $this->_Havings);
  707. if (count($this->_OrderBys) > 0)
  708. $Sql .= "\norder by ".implode(', ', $this->_OrderBys);
  709. if (is_numeric($this->_Limit)) {
  710. $Sql .= "\n";
  711. $Sql = $this->GetLimit($Sql, $this->_Limit, $this->_Offset);
  712. }
  713. return $Sql;
  714. }
  715. /**
  716. * Returns a truncate statement for this database engine.
  717. *
  718. * @param string $Table The name of the table to updated data in.
  719. */
  720. public function GetTruncate($Table) {
  721. trigger_error(ErrorMessage('The selected database engine does not perform the requested task.', $this->ClassName, 'GetTruncate'), E_USER_ERROR);
  722. }
  723. /**
  724. * Returns an update statement for the specified table with the provided
  725. * $Data.
  726. *
  727. * @param array $Tables The names of the tables to updated data in.
  728. * @param array $Data An associative array of FieldName => Value pairs that should be inserted
  729. * $Table.
  730. * @param mixed $Where A where clause (or array containing multiple where clauses) to be applied
  731. * to the where portion of the update statement.
  732. */
  733. public function GetUpdate($Tables, $Data, $Where) {
  734. trigger_error(ErrorMessage('The selected database engine does not perform the requested task.', $this->ClassName, 'GetUpdate'), E_USER_ERROR);
  735. }
  736. /**
  737. * Builds the select statement and runs the query, returning a result
  738. * object. Allows a where clause, limit, and offset to be added directly.
  739. *
  740. * @param string $Table The table from which to select data. Adds to the $this->_Froms collection.
  741. * @param mixed $Where Adds to the $this->_Wheres collection using $this->Where();
  742. * @param string $OrderFields A string of fields to be ordered.
  743. * @param string $OrderDirection The direction of the sort.
  744. * @param int $Limit The number of records to limit the query to.
  745. * @param int $PageNumber The offset where the query results should begin.
  746. * @return Gdn_DataSet The data returned by the query.
  747. */
  748. public function GetWhere($Table = '', $Where = FALSE, $OrderFields = '', $OrderDirection = 'asc', $Limit = FALSE, $PageNumber = FALSE) {
  749. if ($Table != '') {
  750. //$this->MapAliases($Table);
  751. $this->From($Table);
  752. }
  753. if ($Where !== FALSE)
  754. $this->Where($Where);
  755. if ($OrderFields != '')
  756. $this->OrderBy($OrderFields, $OrderDirection);
  757. if ($Limit !== FALSE) {
  758. if ($PageNumber == FALSE || $PageNumber < 1)
  759. $PageNumber = 1;
  760. $Offset = ($PageNumber - 1) * $Limit;
  761. $this->Limit($Limit, $Offset);
  762. }
  763. $Result = $this->Query($this->GetSelect());
  764. return $Result;
  765. }
  766. /**
  767. * Builds the select statement and runs the query, returning a result
  768. * object. Allows a like clause, limit, and offset to be added directly.
  769. *
  770. * @param string $Table The table from which to select data. Adds to the $this->_Froms collection.
  771. * @param mixed $Like Adds to the $this->_Wheres collection using $this->Like();
  772. * @param string $OrderFields A string of fields to be ordered.
  773. * @param string $OrderDirection The direction of the sort.
  774. * @param int $Limit The number of records to limit the query to.
  775. * @param int $PageNumber The offset where the query results should begin.
  776. */
  777. public function GetWhereLike($Table = '', $Like = FALSE, $OrderFields = '', $OrderDirection = 'asc', $Limit = FALSE, $PageNumber = FALSE) {
  778. if ($Table != '') {
  779. $this->MapAliases($Table);
  780. $this->From($Table);
  781. }
  782. if ($Like !== FALSE)
  783. $this->Like($Like);
  784. if ($OrderFields != '')
  785. $this->OrderBy($OrderFields, $OrderDirection);
  786. if ($Limit !== FALSE) {
  787. if ($PageNumber == FALSE || $PageNumber < 1)
  788. $PageNumber = 1;
  789. $Offset = ($PageNumber - 1) * $Limit;
  790. $this->Limit($Limit, $Offset);
  791. }
  792. $Result = $this->Query($this->GetSelect());
  793. return $Result;
  794. }
  795. /**
  796. * Adds to the $this->_GroupBys collection.
  797. *
  798. * @param mixed $Fields An array of field names (or a comma-delimited list of field names) to be
  799. * grouped by.
  800. */
  801. public function GroupBy($Fields = NULL) {
  802. if(is_null($Fields)) {
  803. // Group by every item in the select that isn't a function.
  804. foreach($this->_Selects as $Alias => $Select) {
  805. if(ArrayValue('Function', $Select) == '') {
  806. $this->_GroupBys[] = $Select['Field'];
  807. }
  808. }
  809. return $this;
  810. }
  811. if (is_string($Fields))
  812. $Fields = explode(',', $Fields);
  813. foreach ($Fields as $Field) {
  814. $Field = trim($Field);
  815. if ($Field != '')
  816. $this->_GroupBys[] = $this->EscapeIdentifier($Field);
  817. }
  818. return $this;
  819. }
  820. /**
  821. * Adds to the $this->_Havings collection.
  822. *
  823. * This is the most basic having that adds a freeform string of text.
  824. * It should be used only in conjunction with methods that properly escape the sql.
  825. *
  826. * @param string $Sql The condition to add.
  827. * @return Gdn_DatabaseDriver $this
  828. */
  829. protected function _Having($Sql) {
  830. // Figure out the concatenation operator.
  831. $Concat = '';
  832. if(count($this->_Havings) > 0) {
  833. $Concat = ' ' . $this->_WhereConcat . ' ';
  834. }
  835. // Revert the concat back to 'and'.
  836. $this->_WhereConcat = $this->_WhereConcatDefault;
  837. $this->_Havings[] = $Concat . $Sql;
  838. return $this;
  839. }
  840. /**
  841. * Adds to the $this->_Havings collection. Called by $this->Having() and
  842. * $this->OrHaving().
  843. *
  844. * @param mixed $Field The name of the field (or array of field names) in the having clause.
  845. * @param string $Value The string on the right side of the having comparison.
  846. * @param boolean $EscapeSql A boolean value indicating if $this->EscapeSql method should be called
  847. * on $Field.
  848. * @param boolean $EscapeString A boolean value indicating if $this->EscapeString method should be called
  849. * on $Value.
  850. */
  851. function Having($Field, $Value = '', $EscapeField = TRUE, $EscapeValue = TRUE) {
  852. if (!is_array($Field))
  853. $Field = array($Field => $Value);
  854. foreach ($Field as $ChildField => $ChildValue) {
  855. $Expr = $this->ConditionExpr($ChildField, $ChildValue, $EscapeField, $EscapeValue);
  856. $this->_Having($Expr);
  857. }
  858. return $this;
  859. }
  860. public function History($UpdateFields = TRUE, $InsertFields = FALSE) {
  861. $UserID = GetValueR('User.UserID', Gdn::Session(), Gdn::Session()->UserID);
  862. if($InsertFields)
  863. $this->Set('DateInserted', Gdn_Format::ToDateTime())->Set('InsertUserID', $UserID);
  864. if($UpdateFields)
  865. $this->Set('DateUpdated', Gdn_Format::ToDateTime())->Set('UpdateUserID', $UserID);
  866. return $this;
  867. }
  868. /**
  869. * Returns the last identity to be inserted into the database at
  870. * $this->_Connection.
  871. */
  872. public function Identity() {
  873. return $this->Connection()->lastInsertId();
  874. }
  875. /**
  876. * Returns information about the database. Values include: Engine, Version,
  877. * DatabaseName.
  878. *
  879. * @param string $Request The piece of information being requested. Accepted values are: Engine,
  880. * Version, and DatabaseName.
  881. */
  882. public function Information($Request) {
  883. if (array_key_exists($Request, $this->_DatabaseInfo) === FALSE) {
  884. if ($Request == 'Version') {
  885. $this->_DatabaseInfo['Version'] = $this->Version();
  886. } else {
  887. $this->_DatabaseInfo['HostName'] = Gdn::Config('Database.Host', '');
  888. $this->_DatabaseInfo['DatabaseName'] = Gdn::Config('Database.Name', '');
  889. }
  890. }
  891. if (array_key_exists($Request, $this->_DatabaseInfo) === TRUE) {
  892. return $this->_DatabaseInfo[$Request];
  893. } else {
  894. return '';
  895. }
  896. }
  897. /**
  898. * Builds the insert statement and runs the query, returning a result
  899. * object.
  900. *
  901. * @param string $Table The table to which data should be inserted.
  902. * @param mixed $Set An associative array (or object) of FieldName => Value pairs that should
  903. * be inserted, or an array of FieldName values that should have values
  904. * inserted from $Select.
  905. * @param string $Select A select query that will fill the FieldNames specified in $Set.
  906. */
  907. public function Insert($Table = '', $Set = NULL, $Select = '') {
  908. if (count($Set) == 0 && count($this->_Sets) == 0) {
  909. return FALSE;
  910. }
  911. if (!is_null($Set) && $Select == '' && !array_key_exists(0, $Set)) {
  912. $this->Set($Set);
  913. $Set = $this->_Sets;
  914. }
  915. if ($Table == '') {
  916. if (!isset($this->_Froms[0]))
  917. return FALSE;
  918. $Table = $this->_Froms[0];
  919. }
  920. $Sql = $this->GetInsert($this->EscapeIdentifier($this->Database->DatabasePrefix.$Table), $Set, $Select);
  921. $Result = $this->Query($Sql, 'insert');
  922. return $Result;
  923. }
  924. /**
  925. * Inserts or updates values in the table depending on whether they are already there.
  926. *
  927. * @param string $Table The name of the table to insert/update.
  928. * @param array $Set The columns to update.
  929. * @param array $Where The columns to find the row to update.
  930. * If a row is not found then one is inserted and the items in this array are merged with $Set.
  931. */
  932. public function Replace($Table = '', $Set = NULL, $Where, $CheckExisting = FALSE) {
  933. if(count($this->_Sets) > 0) {
  934. foreach($this->_Sets as $Key => $Value) {
  935. if(array_key_exists($Value, $this->_NamedParameters)) {
  936. $Set[$Key] = $this->_NamedParameters[$Value];
  937. unset($this->_NamedParameters[$Value]);
  938. } else {
  939. $Set[$Key] = $Value;
  940. }
  941. }
  942. $this->_Sets = array();
  943. }
  944. // Check to see if there is a row in the table like this.
  945. if ($CheckExisting) {
  946. $Row = $this->GetWhere($Table, $Where)->FirstRow(DATASET_TYPE_ARRAY);
  947. $Update = FALSE;
  948. if ($Row) {
  949. foreach ($Set as $Key => $Value) {
  950. $Key = trim($Key, '`');
  951. if (in_array($Key, array('DateInserted', 'InsertUserID', 'DateUpdated', 'UpdateUserID')))
  952. continue;
  953. if ($Row[$Key] != $Value) {
  954. $Update = TRUE;
  955. break;
  956. }
  957. }
  958. if (!$Update)
  959. return;
  960. }
  961. } else {
  962. $Count = $this->GetCount($Table, $Where);
  963. $Update = $Count > 0;
  964. }
  965. if($Update) {
  966. // Update the table.
  967. $this->Put($Table, $Set, $Where);
  968. } else {
  969. // Insert the table.
  970. $Set = array_merge($Set, $Where);
  971. $this->Insert($Table, $Set);
  972. }
  973. }
  974. /**
  975. * The table(s) to which this query should join. Returns this object for
  976. * chaining purposes.
  977. *
  978. * @param string $TableName The name of a single table to join to.
  979. * @param string $On The conditions on which the join should occur.
  980. * ie. "user.role_id = role.id"
  981. * @param string $Join The type of join to be made. Accepted values are:
  982. * 'inner', 'outer', 'left', 'right', 'left outer', and 'right outer'.
  983. * @return Gdn_SQLDriver
  984. */
  985. public function Join($TableName, $On, $Join = '') {
  986. $Join = strtolower(trim($Join));
  987. if ($Join != '' && !in_array($Join, array('inner', 'outer', 'left', 'right', 'left outer', 'right outer'), TRUE))
  988. $Join = '';
  989. // Add the table prefix to any table specifications in the clause
  990. // echo '<div>'.$TableName.' ---> '.$this->EscapeSql($this->Database->DatabasePrefix.$TableName, TRUE).'</div>';
  991. if($this->Database->DatabasePrefix) {
  992. $TableName = $this->MapAliases($TableName);
  993. //$Aliases = array_keys($this->_AliasMap);
  994. //$Regex = '';
  995. //foreach ($Aliases as $Alias) {
  996. // $Regex .= '(?<! '.$Alias.')';
  997. //}
  998. //$Regex = '/(\w+'.$Regex.'\.)/';
  999. //$On = preg_replace($Regex, $this->Database->DatabasePrefix.'$1', ' '.$On);
  1000. }
  1001. $JoinClause = ltrim($Join . ' join ') . $this->EscapeIdentifier($TableName, TRUE) . ' on ' . $On;
  1002. $this->_Joins[] = $JoinClause;
  1003. return $this;
  1004. }
  1005. /**
  1006. * A convenience method for Gdn_DatabaseDriver::Join that makes the join type 'left.'
  1007. * @see Gdn_DatabaseDriver::Join()
  1008. */
  1009. public function LeftJoin($TableName, $On) {
  1010. return $this->Join($TableName, $On, 'left');
  1011. }
  1012. /**
  1013. * Adds to the $this->_Wheres collection. Used to generate the LIKE portion
  1014. * of a query. Called by $this->Like(), $this->NotLike()
  1015. *
  1016. * @param mixed $Field The field name (or array of field name => match values) to search in for
  1017. * a like $Match.
  1018. * @param string $Match The value to try to match using a like statement in $Field.
  1019. * @param string $Concat The concatenation operator for the items being added to the like in
  1020. * clause.
  1021. * @param string $Side A string indicating which side of the match to place asterisk operators.
  1022. * Accepted values are left, right, both, none. Default is both.
  1023. * @param string $Op Either 'like' or 'not like' clause.
  1024. * @return Gdn_SQLDriver
  1025. */
  1026. public function Like($Field, $Match = '', $Side = 'both', $Op = 'like') {
  1027. if (!is_array($Field))
  1028. $Field = array($Field => $Match);
  1029. foreach ($Field as $SubField => $SubValue) {
  1030. $SubField .= ' ' . $Op . ' ';
  1031. switch($Side) {
  1032. case 'left':
  1033. $SubValue = '%' . $SubValue;
  1034. break;
  1035. case 'right':
  1036. $SubValue .= '%';
  1037. break;
  1038. case 'both':
  1039. if(strlen($Match) == 0)
  1040. $SubValue = '%';
  1041. else
  1042. $SubValue = '%' . $SubValue . '%';
  1043. break;
  1044. }
  1045. $Expr = $this->ConditionExpr($SubField, $SubValue);
  1046. $this->_Where($Expr);
  1047. }
  1048. return $this;
  1049. }
  1050. /**
  1051. * Sets the limit (and offset optionally) for the query.
  1052. *
  1053. * @param int $Limit The number of records to limit the query to.
  1054. * @param int $Offset The offset where the query results should begin.
  1055. */
  1056. public function Limit($Limit, $Offset = FALSE) {
  1057. $this->_Limit = $Limit;
  1058. if ($Offset !== FALSE)
  1059. $this->_Offset = $Offset;
  1060. return $this;
  1061. }
  1062. /**
  1063. * Takes a provided table specification and parses out any table aliases
  1064. * provided, placing them in an alias mapping array. Returns the table
  1065. * specification with any table prefix prepended.
  1066. *
  1067. * @param string $TableString The string specification of the table. ie.
  1068. * "tbl_User as u" or "user u".
  1069. * @return string
  1070. */
  1071. public function MapAliases($TableString) {
  1072. // Make sure all tables have an alias.
  1073. if(strpos($TableString, ' ') === FALSE) {
  1074. $TableString .= ' ' . $TableString;
  1075. }
  1076. // Map the alias to the alias mapping array
  1077. $TableString = trim(preg_replace('/\s+as\s+/i', ' ', $TableString));
  1078. $Alias = strrchr($TableString, " ");
  1079. $TableName = substr($TableString, 0, strlen($TableString) - strlen($Alias));
  1080. // If no alias was specified then it will be set to the tablename.
  1081. $Alias = trim($Alias);
  1082. if(strlen($Alias) == 0) {
  1083. $Alias = $TableName;
  1084. $TableString .= ' ' . $Alias;
  1085. }
  1086. //$this->_AliasMap[$Alias] = $TableName;
  1087. // Return the string with the database table prefix prepended
  1088. return $this->Database->DatabasePrefix . $TableString;
  1089. }
  1090. /**
  1091. * A convenience method for Gdn_DatabaseDriver::Like that changes the operator to 'not like.'
  1092. * @see Gdn_DatabaseDriver::Like()
  1093. */
  1094. public function NotLike($Field, $Match = '', $Side = 'both') {
  1095. return $this->Like($Field, $Match, $Side, 'not like');
  1096. }
  1097. /**
  1098. * Takes a parameter name and makes sure it is cleaned up to be used as a
  1099. * named parameter in a pdo prepared statement.
  1100. * @param string $Name The name of the parameter to cleanup
  1101. * @param boolean $CreateNew Wether or not this is a new or existing parameter.
  1102. * @return string The cleaned up named parameter name.
  1103. */
  1104. public function NamedParameter($Name, $CreateNew = FALSE, $Value = NULL) {
  1105. // Format the parameter name so it is safe for sql
  1106. $NiceName = ':'.preg_replace('/([^\w\d_-])/', '', $Name); // Removes everything from the string except letters, numbers, dashes, and underscores
  1107. if($CreateNew) {
  1108. // Make sure that the new name doesn't already exist.
  1109. $NumberedName = $NiceName;
  1110. $i = 0;
  1111. while (array_key_exists($NumberedName, $this->_NamedParameters)) {
  1112. $NumberedName = $NiceName.$i;
  1113. ++$i;
  1114. }
  1115. $NiceName = $NumberedName;
  1116. }
  1117. if(!is_null($Value)) {
  1118. $this->_NamedParameters[$NiceName] = $Value;
  1119. }
  1120. return $NiceName;
  1121. }
  1122. /**
  1123. * Allows a query to be called without resetting the object.
  1124. * @param boolean $Reset Whether or not to reset this object when the next query executes.
  1125. * @param boolean $OneTime Whether or not this will apply for only the next query or for all subsequent queries.
  1126. */
  1127. public function NoReset($NoReset = TRUE, $OneTime = TRUE) {
  1128. $_NoReset = $NoReset ? ($OneTime ? 1 : 2) : 0;
  1129. return $this;
  1130. }
  1131. /**
  1132. * Sets the offset for the query.
  1133. *
  1134. * @param int $Offset The offset where the query results should begin.
  1135. */
  1136. public function Offset($Offset) {
  1137. $this->_Offset = $Offset;
  1138. return $this;
  1139. }
  1140. /**
  1141. * Adds to the $this->_OrderBys collection.
  1142. *
  1143. * @param string $Fields A string of fields to be ordered.
  1144. * @param string $Direction The direction of the sort.
  1145. */
  1146. public function OrderBy($Fields, $Direction = 'asc') {
  1147. if ($Direction != 'asc')
  1148. $Direction = 'desc';
  1149. $this->_OrderBys[] = $this->EscapeIdentifier($Fields, TRUE).' '.$Direction;
  1150. return $this;
  1151. }
  1152. /**
  1153. * Adds to the $this->_Havings collection. Concatenates multiple calls with OR.
  1154. *
  1155. * @param mixed $Field The name of the field (or array of field names) in the having clause.
  1156. * @param string $Value The string on the right side of the having comparison.
  1157. * @param boolean $EscapeField A boolean value indicating if $this->EscapeSql method should be called
  1158. * on $Field.
  1159. * @param boolean $EscapeValue A boolean value indicating if $this->EscapeString method should be called
  1160. * on $Value.
  1161. * @return Gdn_DatabaseDriver $this.
  1162. * @see Gdn_DatabaseDriver::Having()
  1163. */
  1164. function OrHaving($Field, $Value = '', $EscapeField = TRUE, $EscapeValue = TRUE) {
  1165. return $this->OrOp()->Having($Field, $Value, $EscapeField, $EscapeValue);
  1166. }
  1167. /**
  1168. * A convenience method that calls Gdn_DatabaseDriver::Like with concatenated with an 'or.'
  1169. * @See Gdn_DatabaseDriver::Like()
  1170. */
  1171. public function OrLike($Field, $Match = '', $Side = 'both', $Op = 'like') {
  1172. if (!is_array($Field))
  1173. $Field = array($Field => $Match);
  1174. foreach ($Field as $f => $v) {
  1175. $this->OrOp()->Like($f, $v, $Side, $Op);
  1176. }
  1177. return $this;
  1178. // return $this->OrOp()->Like($Field, $Match, $Side, $Op);
  1179. }
  1180. /** A convenience method for Gdn_DatabaseDriver::Like that changes the operator to 'not like,'
  1181. * and is concatenated with an 'or.'
  1182. * @see Gdn_DatabaseDriver::NotLike()
  1183. * @see GenricDriver::Like()
  1184. */
  1185. public function OrNotLike($Field, $Match = '', $Side = 'both') {
  1186. return $this->OrLike($Field, $Match, $Side, 'not like');
  1187. }
  1188. /**
  1189. * Concat the next where expression with an 'or' operator.
  1190. *
  1191. * @param boolean $SetDefault Whether or not the 'or' is one time, or will revert.
  1192. * @return Gdn_DatabaseDriver $this
  1193. * @see Gdn_DatabaseDriver::AndOp()
  1194. */
  1195. public function OrOp($SetDefault = FALSE) {
  1196. $this->_WhereConcat = 'or';
  1197. if($SetDefault) {
  1198. $this->_WhereConcatDefault = 'or';
  1199. }
  1200. return $this;
  1201. }
  1202. /**
  1203. * @link Gdn_DatabaseDriver::Where()
  1204. */
  1205. public function OrWhere($Field, $Value = NULL, $EscapeFieldSql = TRUE, $EscapeValueSql = TRUE) {
  1206. return $this->OrOp()->Where($Field, $Value, $EscapeFieldSql, $EscapeValueSql);
  1207. }
  1208. /**
  1209. * A convienience method for Gdn_DatabaseDriver::WhereExists() concatenates with an 'or.'
  1210. * @see Gdn_DatabaseDriver::WhereExists()
  1211. */
  1212. public function OrWhereExists($SqlDriver, $Op = 'exists') {
  1213. return $this->OrOp()->WhereExists($SqlDriver, $Op);
  1214. }
  1215. /**
  1216. * @ling Gdn_DatabaseDriver::WhereIn()
  1217. */
  1218. public function OrWhereIn($Field, $Values) {
  1219. return $this->OrOp()->WhereIn($Field, $Values);
  1220. }
  1221. /**
  1222. * A convienience method for Gdn_DatabaseDriver::WhereExists() that changes the operator to 'not exists,'
  1223. * and concatenates with an 'or.'
  1224. * @see Gdn_DatabaseDriver::WhereExists()
  1225. * @see Gdn_DatabaseDriver::WhereNotExists()
  1226. */
  1227. public function OrWhereNotExists($SqlDriver) {
  1228. return $this->OrWhereExists($SqlDriver, 'not exists');
  1229. }
  1230. /**
  1231. * A convenience method for Gdn_DatabaseDriver::WhereIn() that changes the operator to 'not in,'
  1232. * and concatenates with an 'or.'
  1233. * @see Gdn_DatabaseDriver::WhereIn()
  1234. * @see Gdn_DatabaseDriver::WhereNotIn()
  1235. */
  1236. public function OrWhereNotIn($Field, $Values) {
  1237. return $this->OrOp()->WhereNotIn($Field, $Values);
  1238. }
  1239. /**
  1240. * Parses an expression for use in where clauses.
  1241. *
  1242. * @param string $Expr The expression to parse.
  1243. * @param string $Name A name to give the parameter if $Expr becomes a named parameter.
  1244. * @return string The parsed expression.
  1245. */
  1246. protected function _ParseExpr($Expr, $Name = NULL, $EscapeExpr = FALSE) {
  1247. $Result = '';
  1248. $C = substr($Expr, 0, 1);
  1249. if($C === '=' && $EscapeExpr === FALSE) {
  1250. // This is a function call. Each parameter has to be parsed.
  1251. $FunctionArray = preg_split('/(\[[^\]]+\])/', substr($Expr, 1), -1, PREG_SPLIT_DELIM_CAPTURE);
  1252. for($i = 0; $i < count($FunctionArray); $i++) {
  1253. $Part = $FunctionArray[$i];
  1254. if(substr($Part, 1) == '[') {
  1255. // Translate the part of the function call.
  1256. $Part = $this->_FieldExpr(substr($Part, 1, strlen($Part) - 2), $Name);

Large files files are truncated, but you can click here to view the full file