public function canParseTokens(SQLTokenIterator $tokens, &$checkFlags = 0) { $previousIndex = $tokens->getIndex(); if (!$tokens->seekTokenNum(SqlToken::T_IN(), TokenIterator::NEXT, [SqlToken::T_NOT()])) { $tokens->seekIndex($previousIndex); return false; } $result = is_int($tokens->isTokenText('(')); $tokens->seekIndex($previousIndex); return $result; }
public function canParseTokens(SQLTokenIterator $tokens) { $indexBefore = $tokens->getIndex(); $tokens->seekIndex($tokens->getExclusiveTokenIndex()); if (!in_array((int) (string) $tokens->getCurrentTokenNumber(), [T_STRING, SqlToken::T_DATABASE]) && !$tokens->getCurrentTokenNumber() instanceof Token) { $tokens->seekIndex($indexBefore); return false; } $paranthesisExist = is_int($tokens->isTokenText('(')); $tokens->seekIndex($indexBefore); return $paranthesisExist; }
public function convertSqlToJob(SQLTokenIterator $tokens) { $parts = array(); do { $tokens->seekIndex($tokens->getExclusiveTokenIndex()); $part = $tokens->getCurrentTokenString(); if ($part[0] === '`' && $part[strlen($part) - 1] === '`') { $part = substr($part, 1, strlen($part) - 2); } $parts[] = $part; } while ($tokens->seekTokenText(".") && !$tokens->isTokenText('*')); return TableSpecifier::factory(implode(".", $parts)); }
public function canParseTokens(SQLTokenIterator $tokens, $from = TokenIterator::NEXT) { return is_int($tokens->isTokenText('(', $from)); }
public function convertSqlToJob(SQLTokenIterator $tokens) { // catch both cases when select is current AND when its next token. $tokens->seekTokenNum(SqlToken::T_SELECT()); if ($tokens->getCurrentTokenNumber() !== SqlToken::T_SELECT()) { throw new ErrorException("Tried to convert select-sql to job when sql-token-iterator does not point to T_SELECT!"); } $entitySelect = new SelectStatement(); ### SPECIAL FLAGS foreach ([[SpecialFlags::FLAG_ALL(), SqlToken::T_ALL()], [SpecialFlags::FLAG_DISTINCT(), SqlToken::T_DISTINCT()], [SpecialFlags::FLAG_DISTINCTROW(), SqlToken::T_DISTINCTROW()], [SpecialFlags::FLAG_HIGH_PRIORITY(), SqlToken::T_HIGH_PRIORITY()], [SpecialFlags::FLAG_STRAIGHT_JOIN(), SqlToken::T_STRAIGHT_JOIN()], [SpecialFlags::FLAG_SQL_SMALL_RESULT(), SqlToken::T_SQL_SMALL_RESULT()], [SpecialFlags::FLAG_SQL_BIG_RESULT(), SqlToken::T_SQL_BIG_RESULT()], [SpecialFlags::FLAG_SQL_BUFFER_RESULT(), SqlToken::T_SQL_BUFFER_RESULT()], [SpecialFlags::FLAG_SQL_CACHE(), SqlToken::T_SQL_CACHE()], [SpecialFlags::FLAG_SQL_NO_CACHE(), SqlToken::T_SQL_NO_CACHE()], [SpecialFlags::FLAG_SQL_CALC_FOUND_ROWS(), SqlToken::T_SQL_CALC_FOUND_ROWS()]] as $pair) { list($flagValue, $tokenNum) = $pair; if ($tokens->seekTokenNum($tokenNum)) { $entitySelect->addFlag($flagValue); } } ### COLLECT COLUMNS do { try { switch (true) { # parse jokers like: fooTable.* case is_int($tokens->isTokenText('*', TokenIterator::NEXT, [T_STRING, '.'])): if ($this->tableParser->canParseTokens($tokens)) { $tableFilter = $this->tableParser->convertSqlToJob($tokens); } else { $tableFilter = null; } $tokens->seekTokenText('*', TokenIterator::NEXT, [T_STRING, '.']); $entitySelect->addColumnAllTable($tableFilter); break; case $this->valueParser->canParseTokens($tokens): $value = $this->valueParser->convertSqlToJob($tokens); if ($tokens->seekTokenNum(T_STRING, TokenIterator::NEXT, [SqlToken::T_AS()])) { $entitySelect->addColumnValue($value, $tokens->getCurrentTokenString()); } else { $entitySelect->addColumnValue($value); } break; default: throw new MalformedSqlException("Non-column-sql found in column-part of select!", $tokens); } } catch (MalformedSqlException $exception) { throw new MalformedSqlException($exception->getMessage(), $tokens); } } while ($tokens->seekTokenText(',')); ### COLLECT TABLES if ($tokens->seekTokenNum(SqlToken::T_FROM())) { if (!$this->joinParser->canParseTokens($tokens)) { throw new MalformedSqlException("Missing valid join definition after FROM in SELECT statement!", $tokens); } $entitySelect->setJoinDefinition($this->joinParser->convertSqlToJob($tokens)); } ### PREPENDED CONDITION (WHERE) if ($tokens->seekTokenNum(SqlToken::T_WHERE())) { if (!$this->valueParser->canParseTokens($tokens)) { throw new MalformedSqlException("Missing condition for WHERE clause in SELECT statement!", $tokens); } $entitySelect->setCondition($this->valueParser->convertSqlToJob($tokens)); } ### GROUP if ($tokens->seekTokenNum(SqlToken::T_GROUP())) { if (!$tokens->seekTokenNum(SqlToken::T_BY())) { throw new MalformedSqlException("Missing BY after GROUP in SELECT statement!", $tokens); } do { $groupingDefinition = new GroupingDefinition(); if (!$this->columnParser->canParseTokens($tokens)) { throw new MalformedSqlException("Invalid grouping value in SELECT statement!!", $tokens); } $groupingDefinition->setValue($this->columnParser->convertSqlToJob($tokens)); if ($tokens->seekTokenNum(SqlToken::T_DESC())) { $groupingDefinition->setDirection(SqlToken::T_DESC()); } elseif ($tokens->seekTokenNum(SqlToken::T_ASC())) { $groupingDefinition->setDirection(SqlToken::T_ASC()); } $entitySelect->addGrouping($groupingDefinition); } while ($tokens->seekTokenText(',')); } ### APPENDED CONDITION (HAVING) if ($tokens->seekTokenNum(SqlToken::T_HAVING())) { if (!$this->valueParser->canParseTokens($tokens)) { throw new MalformedSqlException("Missing condition for WHERE clause in SELECT statement!", $tokens); } $condition = new ConditionJob(); $condition->setFirstParameter($this->valueParser->convertSqlToJob($tokens)); $entitySelect->setResultFilter($condition); } ### ORDER if ($tokens->seekTokenNum(SqlToken::T_ORDER())) { if (!$tokens->seekTokenNum(SqlToken::T_BY())) { throw new MalformedSqlException("Missing BY after ORDER on SELECT statement!", $tokens); } do { if (!$this->valueParser->canParseTokens($tokens)) { throw new MalformedSqlException("Missing value for ORDER BY part on SELECT statement!", $tokens); } $orderValue = $this->valueParser->convertSqlToJob($tokens); if ($tokens->seekTokenNum(SqlToken::T_DESC())) { $entitySelect->addOrderColumn($orderValue, SqlToken::T_DESC()); } else { $tokens->seekTokenNum(SqlToken::T_ASC()); $entitySelect->addOrderColumn($orderValue, SqlToken::T_ASC()); } } while ($tokens->seekTokenText(',')); } ### LIMIT if ($tokens->seekTokenNum(SqlToken::T_LIMIT())) { if (!$tokens->seekTokenNum(T_NUM_STRING)) { throw new MalformedSqlException("Missing offset number for LIMIT part in SELECT statement!", $tokens); } $entitySelect->setLimitOffset((int) $tokens->getCurrentTokenString()); if ($tokens->seekTokenText(',')) { if (!$tokens->seekTokenNum(T_NUM_STRING)) { throw new MalformedSqlException("Missing length number for LIMIT part in SELECT statement!", $tokens); } $entitySelect->setLimitRowCount((int) $tokens->getCurrentTokenString()); } } ### PROCEDURE if ($tokens->seekTokenNum(SqlToken::T_PROCEDURE())) { if (!$functionParser->canParseTokens($tokens)) { throw new MalformedSqlException("Missing valid procedure specifier after PROCEDURE!", $tokens); } $entitySelect->setProcedure($functionParser->convertSqlToJob($tokens)); } ### INTO OUTFILE|DUMPFILE if ($tokens->seekTokenNum(SqlToken::T_INTO())) { if (!$tokens->seekTokenNum(SqlToken::T_OUTFILE()) && !$tokens->seekTokenNum(SqlToken::T_DUMPFILE())) { throw new MalformedSqlException("Missing OUTFILE or DUMPFILE after INTO!", $tokens); } if (!$tokens->seekTokenNum(T_CONSTANT_ENCAPSED_STRING)) { throw new MalformedSqlException("Missing escaped string after INTO OUTFILE!"); } $entitySelect->setIntoOutFile($tokens->seekTokenText($searchToken)); } ### FOR UPDATE if ($tokens->seekTokenNum(SqlToken::T_FOR())) { if (!$tokens->seekTokenNum(SqlToken::T_UPDATE())) { throw new MalformedSqlException("Missing UPDATE after FOR on FOR UPDATE parameter in SELECT statement!", $tokens); } $entitySelect->setIsForUpdate(true); } ### LOCK IN SHARE MODE if ($tokens->seekTokenNum(SqlToken::T_LOCK())) { if (!$tokens->seekTokenNum(SqlToken::T_IN())) { throw new MalformedSqlException("Missing UPDATE after FOR on FOR UPDATE parameter in SELECT statement!", $tokens); } if (!$tokens->seekTokenNum(SqlToken::T_SHARE())) { throw new MalformedSqlException("Missing UPDATE after FOR on FOR UPDATE parameter in SELECT statement!", $tokens); } if (!$tokens->seekTokenNum(SqlToken::T_MODE())) { throw new MalformedSqlException("Missing UPDATE after FOR on FOR UPDATE parameter in SELECT statement!", $tokens); } $entitySelect->setIsLockInShareMode(true); } ### UNION if ($tokens->seekTokenNum(SqlToken::T_UNION())) { $isUnionAll = $tokens->seekTokenNum(SqlToken::T_ALL()); $isUnionDistinct = $tokens->seekTokenNum(SqlToken::T_DISTINCT()); $isUnionAll = $isUnionAll || $tokens->seekTokenNum(SqlToken::T_ALL()); if ($isUnionAll && $isUnionDistinct) { throw new MalformedSqlException("UNION cannot be ALL and DISTINCT at the same time!", $tokens); } $isUnionInParenthesis = $tokens->seekTokenText('('); if (!$this->canParseTokens($tokens)) { throw new MalformedSqlException("Missing following SELECT statement after UNION in SELECT statement!", $tokens); } $entitySelect->setUnionSelect($this->convertSqlToJob($tokens), $isUnionDistinct); if ($isUnionInParenthesis && !$tokens->seekTokenText(')')) { throw new MalformedSqlException("Missing ending parenthesis after UNION in SELECT statement!", $tokens); } } return $entitySelect; }
public function convertSqlToJob(SQLTokenIterator $tokens) { if (get_class($this) !== __CLASS__) { throw new ErrorException("Class '" . get_class($this) . "' needs to declare an own method '" . __FUNCTION__ . "'!"); } if (!$this->wasInitialized) { $this->initSqlSubParsers(); } /* @var $jobEntity Job */ $jobEntities = array(); # $tokens->seekIndex(-1); do { while ($tokens->seekTokenText(';')) { } $parserFound = false; foreach ($this->sqlParser as $sqlParser) { /* @var $sqlParser SqlParser */ if ($sqlParser->canParseTokens($tokens)) { $parserFound = true; $jobEntity = $sqlParser->convertSqlToJob($tokens); while ($jobEntity instanceof ParenthesisPart) { $jobEntity = $jobEntity->getContain(); } $jobEntities[] = $jobEntity; break; } } if (!$parserFound) { if (is_null($tokens->getExclusiveTokenNumber()) || $tokens->isAtEnd()) { break; } else { $relevantToken = $tokens->getExclusiveTokenString(); throw new MalformedSqlException("Invalid SQL-statement! (Cannot extract command: '{$relevantToken}')", $tokens); } } } while ($tokens->isTokenText(';')); if (!$tokens->isAtEnd() && $tokens->getExclusiveTokenIndex() !== $tokens->getIndex()) { throw new MalformedSqlException("Overlapping unparsed SQL at the end of statement!", $tokens); } foreach ($jobEntities as $job) { $job->checkPlausibility(); } return $jobEntities; }