PageRenderTime 62ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/library/database/class.sqldriver.php

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

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