PageRenderTime 47ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/classes/base/db/firebird/expression.php

http://github.com/spadefoot/kohana-orm-leap
PHP | 410 lines | 230 code | 21 blank | 159 comment | 63 complexity | 9f0504a4aa819e914767a2bb38a65439 MD5 | raw file
  1. <?php defined('SYSPATH') OR die('No direct script access.');
  2. /**
  3. * Copyright 2011-2012 Spadefoot
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /**
  18. * This class provides a set of functions for preparing a Firebird SQL expression.
  19. *
  20. * @package Leap
  21. * @category Firebird
  22. * @version 2012-05-10
  23. *
  24. * @abstract
  25. */
  26. abstract class Base_DB_Firebird_Expression implements DB_SQL_Expression_Interface {
  27. /**
  28. * This constant represents an opening identifier quote character.
  29. *
  30. * @access public
  31. * @static
  32. * @const string
  33. */
  34. const _OPENING_QUOTE_CHARACTER_ = '"';
  35. /**
  36. * This constant represents a closing identifier quote character.
  37. *
  38. * @access public
  39. * @static
  40. * @const string
  41. */
  42. const _CLOSING_QUOTE_CHARACTER_ = '"';
  43. /**
  44. * This variable stores the data source for which the expression is being
  45. * prepared for.
  46. *
  47. * @access protected
  48. * @var mixed
  49. */
  50. protected $source;
  51. /**
  52. * This function initializes the class with the specified data source.
  53. *
  54. * @access public
  55. * @param mixed $source the data source to be used
  56. */
  57. public function __construct($source) {
  58. $this->source = $source;
  59. }
  60. /**
  61. * This function prepares the specified expression as an alias.
  62. *
  63. * @access public
  64. * @param string $expr the expression to be prepared
  65. * @return string the prepared expression
  66. * @throws Kohana_InvalidArgument_Exception indicates that there is a data type mismatch
  67. */
  68. public function prepare_alias($expr) {
  69. if ( ! is_string($expr)) {
  70. throw new Kohana_InvalidArgument_Exception('Message: Invalid alias token specified. Reason: Token must be a string.', array(':expr' => $expr));
  71. }
  72. return self::_OPENING_QUOTE_CHARACTER_ . trim(preg_replace('/[^a-z0-9$_ ]/i', '', $expr)) . self::_CLOSING_QUOTE_CHARACTER_;
  73. }
  74. /**
  75. * This function prepares the specified expression as a boolean.
  76. *
  77. * @access public
  78. * @param string $expr the expression to be prepared
  79. * @return string the prepared expression
  80. */
  81. public function prepare_boolean($expr) {
  82. return (bool) $expr;
  83. }
  84. /**
  85. * This function prepares the specified expression as a connector.
  86. *
  87. * @access public
  88. * @param string $expr the expression to be prepared
  89. * @return string the prepared expression
  90. */
  91. public function prepare_connector($expr) {
  92. if (is_string($expr)) {
  93. $expr = strtoupper($expr);
  94. switch ($expr) {
  95. case DB_SQL_Connector::_AND_:
  96. case DB_SQL_Connector::_OR_:
  97. return $expr;
  98. break;
  99. }
  100. }
  101. throw new Kohana_InvalidArgument_Exception('Message: Invalid connector token specified. Reason: Token must exist in the enumerated set.', array(':expr' => $expr));
  102. }
  103. /**
  104. * This function prepares the specified expression as an identifier column.
  105. *
  106. * @access public
  107. * @param string $expr the expression to be prepared
  108. * @return string the prepared expression
  109. *
  110. * @see http://www.ispirer.com/wiki/sqlways/interbase-firebird/identifiers
  111. */
  112. public function prepare_identifier($expr) {
  113. if ($expr instanceof DB_Firebird_Select_Builder) {
  114. return DB_SQL_Builder::_OPENING_PARENTHESIS_ . $expr->statement(FALSE) . DB_SQL_Builder::_CLOSING_PARENTHESIS_;
  115. }
  116. else if ($expr instanceof DB_SQL_Expression) {
  117. return $expr->value($this);
  118. }
  119. else if (class_exists('Database_Expression') && ($expr instanceof Database_Expression)) {
  120. return $expr->value();
  121. }
  122. else if ( ! is_string($expr)) {
  123. throw new Kohana_InvalidArgument_Exception('Message: Invalid identifier expression specified. Token: Token must be a string.', array(':expr' => $expr));
  124. }
  125. else if (preg_match('/^SELECT.*$/i', $expr)) {
  126. $expr = rtrim($expr, "; \t\n\r\0\x0B");
  127. return DB_SQL_Builder::_OPENING_PARENTHESIS_ . $expr . DB_SQL_Builder::_CLOSING_PARENTHESIS_;
  128. }
  129. $parts = explode('.', $expr);
  130. foreach ($parts as &$part) {
  131. $part = self::_OPENING_QUOTE_CHARACTER_ . trim(preg_replace('/[^a-z0-9$_ ]/i', '', $part)) . self::_CLOSING_QUOTE_CHARACTER_;
  132. }
  133. $expr = implode('.', $parts);
  134. return $expr;
  135. }
  136. /**
  137. * This function prepares the specified expression as a join type.
  138. *
  139. * @access public
  140. * @param string $expr the expression to be prepared
  141. * @return string the prepared expression
  142. *
  143. * @see http://www.firebirdsql.org/refdocs/langrefupd21-select.html
  144. */
  145. public function prepare_join($expr) {
  146. if (is_string($expr)) {
  147. $expr = strtoupper($expr);
  148. switch ($expr) {
  149. case DB_SQL_JoinType::_CROSS_:
  150. case DB_SQL_JoinType::_INNER_:
  151. case DB_SQL_JoinType::_LEFT_:
  152. case DB_SQL_JoinType::_LEFT_OUTER_:
  153. case DB_SQL_JoinType::_RIGHT_:
  154. case DB_SQL_JoinType::_RIGHT_OUTER_:
  155. case DB_SQL_JoinType::_FULL_:
  156. case DB_SQL_JoinType::_FULL_OUTER_:
  157. case DB_SQL_JoinType::_NATURAL_:
  158. case DB_SQL_JoinType::_NATURAL_INNER_:
  159. case DB_SQL_JoinType::_NATURAL_LEFT_:
  160. case DB_SQL_JoinType::_NATURAL_LEFT_OUTER_:
  161. case DB_SQL_JoinType::_NATURAL_RIGHT_:
  162. case DB_SQL_JoinType::_NATURAL_RIGHT_OUTER_:
  163. return $expr;
  164. break;
  165. }
  166. }
  167. throw new Kohana_InvalidArgument_Exception('Message: Invalid join type token specified. Reason: Token must exist in the enumerated set.', array(':expr' => $expr));
  168. }
  169. /**
  170. * This function prepares the specified expression as a natural number.
  171. *
  172. * @access public
  173. * @param string $expr the expression to be prepared
  174. * @return string the prepared expression
  175. */
  176. public function prepare_natural($expr) {
  177. settype($expr, 'integer');
  178. return abs($expr);
  179. }
  180. /**
  181. * This function prepares the specified expression as a operator.
  182. *
  183. * @access public
  184. * @param string $expr the expression to be prepared
  185. * @param string $group the operator grouping
  186. * @return string the prepared expression
  187. */
  188. public function prepare_operator($expr, $group) {
  189. if (is_string($group) && is_string($expr)) {
  190. $group = strtoupper($group);
  191. $expr = strtoupper($expr);
  192. if ($group == 'COMPARISON') {
  193. switch ($expr) {
  194. case DB_SQL_Operator::_NOT_EQUAL_TO_:
  195. $expr = DB_SQL_Operator::_NOT_EQUIVALENT_;
  196. case DB_SQL_Operator::_NOT_EQUIVALENT_:
  197. case DB_SQL_Operator::_EQUAL_TO_:
  198. case DB_SQL_Operator::_BETWEEN_:
  199. case DB_SQL_Operator::_NOT_BETWEEN_:
  200. case DB_SQL_Operator::_LIKE_:
  201. case DB_SQL_Operator::_NOT_LIKE_:
  202. case DB_SQL_Operator::_LESS_THAN_:
  203. case DB_SQL_Operator::_LESS_THAN_OR_EQUAL_TO_:
  204. case DB_SQL_Operator::_GREATER_THAN_:
  205. case DB_SQL_Operator::_GREATER_THAN_OR_EQUAL_TO_:
  206. case DB_SQL_Operator::_IN_:
  207. case DB_SQL_Operator::_NOT_IN_:
  208. case DB_SQL_Operator::_IS_:
  209. case DB_SQL_Operator::_IS_NOT_:
  210. return $expr;
  211. break;
  212. }
  213. }
  214. else if ($group == 'SET') {
  215. switch ($expr) {
  216. case DB_SQL_Operator::_UNION_:
  217. case DB_SQL_Operator::_UNION_ALL_:
  218. case DB_SQL_Operator::_UNION_DISTINCT_:
  219. return $expr;
  220. break;
  221. }
  222. }
  223. }
  224. throw new Kohana_InvalidArgument_Exception('Message: Invalid operator token specified. Reason: Token must exist in the enumerated set.', array(':group' => $group, ':expr' => $expr));
  225. }
  226. /**
  227. * This function prepare the specified expression as a ordering token.
  228. *
  229. * @access public
  230. * @param string $column the column to be sorted
  231. * @param string $ordering the ordering token that signal whether the
  232. * column will sorted either in ascending or
  233. * descending order
  234. * @param string $nulls the weight to be given to null values
  235. * @return string the prepared clause
  236. *
  237. * @see http://www.firebirdsql.org/refdocs/langrefupd20-select.html#langrefupd20-orderby
  238. */
  239. public function prepare_ordering($column, $ordering, $nulls) {
  240. $column = $this->prepare_identifier($column);
  241. switch (strtoupper($ordering)) {
  242. case 'DESC':
  243. $ordering = 'DESC';
  244. break;
  245. case 'ASC':
  246. default:
  247. $ordering = 'ASC';
  248. break;
  249. }
  250. $expr = "{$column} {$ordering}";
  251. switch (strtoupper($nulls)) {
  252. case 'FIRST':
  253. $expr .= ' NULLS FIRST';
  254. break;
  255. case 'LAST':
  256. $expr .= ' NULLS LAST';
  257. break;
  258. }
  259. return $expr;
  260. }
  261. /**
  262. * This function prepares the specified expression as a parenthesis.
  263. *
  264. * @access public
  265. * @param string $expr the expression to be prepared
  266. * @return string the prepared expression
  267. */
  268. public function prepare_parenthesis($expr) {
  269. if (is_string($expr)) {
  270. switch ($expr) {
  271. case DB_SQL_Builder::_OPENING_PARENTHESIS_:
  272. case DB_SQL_Builder::_CLOSING_PARENTHESIS_:
  273. return $expr;
  274. break;
  275. }
  276. }
  277. throw new Kohana_InvalidArgument_Exception('Message: Invalid parenthesis token specified. Reason: Token must exist in the enumerated set.', array(':expr' => $expr));
  278. }
  279. /**
  280. * This function prepares the specified expression as a value.
  281. *
  282. * @access public
  283. * @param string $expr the expression to be prepared
  284. * @param char $escape the escape character
  285. * @return string the prepared expression
  286. */
  287. public function prepare_value($expr, $escape = NULL) {
  288. if ($expr === NULL) {
  289. return 'NULL';
  290. }
  291. else if ($expr === TRUE) {
  292. return "'1'";
  293. }
  294. else if ($expr === FALSE) {
  295. return "'0'";
  296. }
  297. else if (is_array($expr)) {
  298. $buffer = array();
  299. foreach ($expr as $value) {
  300. $buffer[] = call_user_func_array(array($this, __FUNCTION__), array($value, $escape));
  301. }
  302. return DB_SQL_Builder::_OPENING_PARENTHESIS_ . implode(', ', $buffer) . DB_SQL_Builder::_CLOSING_PARENTHESIS_;
  303. }
  304. else if (is_object($expr)) {
  305. if ($expr instanceof DB_Firebird_Select_Builder) {
  306. return DB_SQL_Builder::_OPENING_PARENTHESIS_ . $expr->statement(FALSE) . DB_SQL_Builder::_CLOSING_PARENTHESIS_;
  307. }
  308. else if ($expr instanceof DB_SQL_Expression) {
  309. return $expr->value($this);
  310. }
  311. else if (class_exists('Database_Expression') && ($expr instanceof Database_Expression)) {
  312. return $expr->value();
  313. }
  314. else if ($expr instanceof Data) {
  315. return "x'" . $expr->as_hexcode() . "'";
  316. }
  317. else {
  318. return self::prepare_value( (string) $expr); // Convert the object to a string
  319. }
  320. }
  321. else if (is_integer($expr)) {
  322. return (int) $expr;
  323. }
  324. else if (is_double($expr)) {
  325. return sprintf('%F', $expr);
  326. }
  327. else if (is_string($expr) && preg_match('/^[0-9]{4}-[0-9]{2}-[0-9]{2}(\s[0-9]{2}:[0-9]{2}:[0-9]{2})?$/', $expr)) { // is_datetime($expr)
  328. return "'{$expr}'";
  329. }
  330. else if (empty($expr)) {
  331. return "''";
  332. }
  333. else {
  334. return DB_Connection_Pool::instance()->get_connection($this->source)->quote($expr, $escape);
  335. }
  336. }
  337. /**
  338. * This function prepares the specified expression as a wildcard.
  339. *
  340. * @access public
  341. * @param string $expr the expression to be prepared
  342. * @return string the prepared expression
  343. */
  344. public function prepare_wildcard($expr) {
  345. if ( ! is_string($expr)) {
  346. throw new Kohana_InvalidArgument_Exception('Message: Invalid wildcard token specified. Reason: Token must be a string.', array(':expr' => $expr));
  347. }
  348. $parts = explode('.', $expr);
  349. $count = count($parts);
  350. for ($i = 0; $i < $count; $i++) {
  351. $parts[$i] = (trim($parts[$i]) != '*')
  352. ? self::_OPENING_QUOTE_CHARACTER_ . trim(preg_replace('/[^a-z0-9$_ ]/i', '', $parts[$i])) . self::_CLOSING_QUOTE_CHARACTER_
  353. : '*';
  354. }
  355. if (isset($parts[$count - 1]) && ($parts[$count - 1] != '*')) {
  356. $parts[] = '*';
  357. }
  358. $expr = implode('.', $parts);
  359. return $expr;
  360. }
  361. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  362. /**
  363. * This variable stores the compiler's XML config file.
  364. *
  365. * @access protected
  366. * @static
  367. * @var XML
  368. */
  369. protected static $xml = NULL;
  370. /**
  371. * This function checks whether the specified token is a reserved keyword.
  372. *
  373. * @access public
  374. * @static
  375. * @param string $token the token to be cross-referenced
  376. * @return boolean whether the token is a reserved keyword
  377. *
  378. * @see http://www.firebirdsql.org/file/documentation/reference_manuals/reference_material/html/langrefupd25-reskeywords-full-reswords.html
  379. */
  380. public static function is_keyword($token) {
  381. if (is_null(self::$xml)) {
  382. self::$xml = XML::load('config/sql/firebird.xml');
  383. }
  384. $token = strtoupper($token);
  385. $nodes = self::$xml->xpath("/sql/dialect[@name='firebird' and @version='2.5']/keywords[keyword = '{$token}']");
  386. return ! empty($nodes);
  387. }
  388. }
  389. ?>