* * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. */ require_once(dirname(__FILE__) . '/AbstractProcessor.php'); require_once(dirname(__FILE__) . '/ExpressionListProcessor.php'); require_once(dirname(__FILE__) . '/../utils/ExpressionType.php'); /** * * This class processes the SELECT expressions. * * @author arothe * */ class SelectExpressionProcessor extends AbstractProcessor { private $expressionListProcessor; public function __construct() { $this->expressionListProcessor = new ExpressionListProcessor(); } /** * This fuction processes each SELECT clause. * We determine what (if any) alias * is provided, and we set the type of expression. */ public function process($expression) { $tokens = $this->splitSQLIntoTokens($expression); $token_count = count($tokens); if ($token_count === 0) { return null; } /* * Determine if there is an explicit alias after the AS clause. * If AS is found, then the next non-whitespace token is captured as the alias. * The tokens after (and including) the AS are removed. */ $base_expr = ""; $stripped = array(); $capture = false; $alias = false; $processed = false; for ($i = 0; $i < $token_count; ++$i) { $token = $tokens[$i]; $upper = strtoupper($token); if ($upper === 'AS') { $alias = array('as' => true, "name" => "", "base_expr" => $token); $tokens[$i] = ""; $capture = true; continue; } if (!$this->isWhitespaceToken($upper)) { $stripped[] = $token; } // we have an explicit AS, next one can be the alias // but also a comment! if ($capture) { if (!$this->isWhitespaceToken($upper) && !$this->isCommentToken($upper)) { $alias['name'] .= $token; array_pop($stripped); } $alias['base_expr'] .= $token; $tokens[$i] = ""; continue; } $base_expr .= $token; } $stripped = $this->expressionListProcessor->process($stripped); // TODO: the last part can also be a comment, don't use array_pop // we remove the last token, if it is a colref, // it can be an alias without an AS $last = array_pop($stripped); if (!$alias && $this->isColumnReference($last)) { // TODO: it can be a comment, don't use array_pop // check the token before the colref $prev = array_pop($stripped); if ($this->isReserved($prev) || $this->isConstant($prev) || $this->isAggregateFunction($prev) || $this->isFunction($prev) || $this->isExpression($prev) || $this->isSubQuery($prev) || $this->isColumnReference($prev) || $this->isBracketExpression($prev)) { $alias = array('as' => false, 'name' => trim($last['base_expr']), 'no_quotes' => $this->revokeQuotation($last['base_expr']), 'base_expr' => trim($last['base_expr'])); // remove the last token array_pop($tokens); $base_expr = join("", $tokens); } } if (!$alias) { $base_expr = join("", $tokens); } else { /* remove escape from the alias */ $alias['no_quotes'] = $this->revokeQuotation($alias['name']); $alias['name'] = trim($alias['name']); $alias['base_expr'] = trim($alias['base_expr']); } // TODO: this is always done with $stripped, how we do it twice? $processed = $this->expressionListProcessor->process($tokens); // if there is only one part, we copy the expr_type // in all other cases we use "expression" as global type $type = ExpressionType::EXPRESSION; $no_quotes = $this->revokeQuotation(trim($base_expr)); if (count($processed) === 1) { if (!$this->isSubQuery($processed[0])) { $type = $processed[0]['expr_type']; $base_expr = $processed[0]['base_expr']; $no_quotes = isset($processed[0]['no_quotes']) ? $processed[0]['no_quotes'] : null; $processed = $processed[0]['sub_tree']; // it can be FALSE } } $result = array(); $result['expr_type'] = $type; $result['alias'] = $alias; $result['base_expr'] = trim($base_expr); if (!empty($no_quotes)) { $result['no_quotes'] = $no_quotes; } $result['sub_tree'] = $processed; return $result; } } ?>