/** * DQL FROM PARSER * parses the from part of the query string * @param string $str * @return void */ public function parse($str) { $str = trim($str); $parts = Doctrine_Tokenizer::bracketExplode($str, 'JOIN'); $operator = false; switch (trim($parts[0])) { case 'INNER': $operator = ':'; case 'LEFT': array_shift($parts); break; } $last = ''; foreach ($parts as $k => $part) { $part = trim($part); if (empty($part)) { continue; } $e = explode(' ', $part); if (end($e) == 'INNER' || end($e) == 'LEFT') { $last = array_pop($e); } $part = implode(' ', $e); foreach (Doctrine_Tokenizer::bracketExplode($part, ',') as $reference) { $reference = trim($reference); $e = explode('.', $reference); if ($operator) { $reference = array_shift($e) . $operator . implode('.', $e); } $table = $this->query->load($reference); } $operator = $last == 'INNER' ? ':' : '.'; } return null; }
/** * DQL CONDITION PARSER * parses the join condition/where/having part of the query string * * @param string $str * @return string */ public function parse($str) { $tmp = trim($str); $parts = Doctrine_Tokenizer::bracketExplode($str, array(' AND '), '(', ')'); if (count($parts) > 1) { $ret = array(); foreach ($parts as $part) { $part = Doctrine_Tokenizer::bracketTrim($part, '(', ')'); $ret[] = $this->parse($part); } $r = implode(' AND ', $ret); } else { $parts = Doctrine_Tokenizer::bracketExplode($str, array(' \\|\\| ', ' OR '), '(', ')'); if (count($parts) > 1) { $ret = array(); foreach ($parts as $part) { $part = Doctrine_Tokenizer::bracketTrim($part, '(', ')'); $ret[] = $this->parse($part); } $r = implode(' OR ', $ret); } else { if (substr($parts[0], 0, 1) == '(' && substr($parts[0], -1) == ')') { return $this->parse(substr($parts[0], 1, -1)); } else { return $this->load($parts[0]); } } } return '(' . $r . ')'; }
public function parseClause($clause) { $e = Doctrine_Tokenizer::bracketExplode($clause, ' '); foreach ($e as $k => $expr) { $e[$k] = $this->parseExpression($expr); } return implode(' ', $e); }
/** * load * returns the parsed query part * * @param string $having * @return string */ public final function load($having) { $e = Doctrine_Tokenizer::bracketExplode($having, ' ', '(', ')'); $r = array_shift($e); $t = explode('(', $r); $count = count($t); $r = $this->parseAggregateFunction($r); $operator = array_shift($e); $value = implode(' ', $e); $r .= ' ' . $operator . ' ' . $value; return $r; }
/** * getLimitSubquery * this is method is used by the record limit algorithm * * when fetching one-to-many, many-to-many associated data with LIMIT clause * an additional subquery is needed for limiting the number of returned records instead * of limiting the number of sql result set rows * * @return string the limit subquery */ public function getLimitSubquery() { $map = reset($this->_aliasMap); $table = $map['table']; $componentAlias = key($this->_aliasMap); // get short alias $alias = $this->getTableAlias($componentAlias); $primaryKey = $alias . '.' . $table->getIdentifier(); // initialize the base of the subquery $subquery = 'SELECT DISTINCT ' . $this->_conn->quoteIdentifier($primaryKey); $driverName = $this->_conn->getAttribute(Doctrine::ATTR_DRIVER_NAME); // pgsql needs the order by fields to be preserved in select clause if ($driverName == 'pgsql') { foreach ($this->parts['orderby'] as $part) { $part = trim($part); $e = Doctrine_Tokenizer::bracketExplode($part, ' '); $part = trim($e[0]); if (strpos($part, '.') === false) { continue; } // don't add functions if (strpos($part, '(') !== false) { continue; } // don't add primarykey column (its already in the select clause) if ($part !== $primaryKey) { $subquery .= ', ' . $part; } } } if ($driverName == 'mysql' || $driverName == 'pgsql') { foreach ($this->_expressionMap as $dqlAlias => $expr) { if (isset($expr[1])) { $subquery .= ', ' . $expr[0] . ' AS ' . $this->aggregateMap[$dqlAlias]; } } } $subquery .= ' FROM'; foreach ($this->parts['from'] as $part) { // preserve LEFT JOINs only if needed if (substr($part, 0, 9) === 'LEFT JOIN') { $e = explode(' ', $part); if (empty($this->parts['orderby']) && empty($this->parts['where'])) { continue; } } $subquery .= ' ' . $part; } // all conditions must be preserved in subquery $subquery .= !empty($this->parts['where']) ? ' WHERE ' . implode(' AND ', $this->parts['where']) : ''; $subquery .= !empty($this->parts['groupby']) ? ' GROUP BY ' . implode(', ', $this->parts['groupby']) : ''; $subquery .= !empty($this->parts['having']) ? ' HAVING ' . implode(' AND ', $this->parts['having']) : ''; $subquery .= !empty($this->parts['orderby']) ? ' ORDER BY ' . implode(', ', $this->parts['orderby']) : ''; // add driver specific limit clause $subquery = $this->_conn->modifyLimitQuery($subquery, $this->parts['limit'], $this->parts['offset']); $parts = Doctrine_Tokenizer::quoteExplode($subquery, ' ', "'", "'"); foreach ($parts as $k => $part) { if (strpos($part, ' ') !== false) { continue; } $part = trim($part, "\"'`"); if ($this->hasTableAlias($part)) { $parts[$k] = $this->_conn->quoteIdentifier($this->generateNewTableAlias($part)); continue; } if (strpos($part, '.') === false) { continue; } preg_match_all("/[a-zA-Z0-9_]+\\.[a-z0-9_]+/i", $part, $m); foreach ($m[0] as $match) { $e = explode('.', $match); $e[0] = $this->generateNewTableAlias($e[0]); $parts[$k] = str_replace($match, implode('.', $e), $parts[$k]); } } if ($driverName == 'mysql' || $driverName == 'pgsql') { foreach ($parts as $k => $part) { if (strpos($part, "'") !== false) { continue; } if (strpos($part, '__') == false) { continue; } preg_match_all("/[a-zA-Z0-9_]+\\_\\_[a-z0-9_]+/i", $part, $m); foreach ($m[0] as $match) { $e = explode('__', $match); $e[0] = $this->generateNewTableAlias($e[0]); $parts[$k] = str_replace($match, implode('__', $e), $parts[$k]); } } } $subquery = implode(' ', $parts); return $subquery; }