/**
  * Function called before the token is processed.
  *
  * Skips the `TABLE` keyword after `RENAME`.
  *
  * @param Parser     $parser The instance that requests parsing.
  * @param TokensList $list   The list of tokens to be parsed.
  * @param Token      $token  The token that is being parsed.
  *
  * @return void
  */
 public function before(Parser $parser, TokensList $list, Token $token)
 {
     if ($token->type === Token::TYPE_KEYWORD && $token->value === 'RENAME') {
         // Checking if it is the beginning of the query.
         $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'TABLE');
     }
 }
Beispiel #2
0
 public function testAdd()
 {
     $list = new TokensList();
     foreach ($this->tokens as $token) {
         $list->add($token);
     }
     $this->assertEquals($this->getList(), $list);
 }
 /**
  * @return string
  */
 public function build()
 {
     $fields = '';
     if (!empty($this->fields)) {
         if (is_array($this->fields)) {
             $fields = CreateDefinition::build($this->fields) . ' ';
         } elseif ($this->fields instanceof ArrayObj) {
             $fields = ArrayObj::build($this->fields);
         }
     }
     if ($this->options->has('DATABASE')) {
         return 'CREATE ' . OptionsArray::build($this->options) . ' ' . Expression::build($this->name) . ' ' . OptionsArray::build($this->entityOptions);
     } elseif ($this->options->has('TABLE')) {
         $partition = '';
         if (!empty($this->partitionBy)) {
             $partition .= "\nPARTITION BY " . $this->partitionBy;
         }
         if (!empty($this->partitionsNum)) {
             $partition .= "\nPARTITIONS " . $this->partitionsNum;
         }
         if (!empty($this->subpartitionBy)) {
             $partition .= "\nSUBPARTITION BY " . $this->subpartitionBy;
         }
         if (!empty($this->subpartitionsNum)) {
             $partition .= "\nSUBPARTITIONS " . $this->subpartitionsNum;
         }
         if (!empty($this->partitions)) {
             $partition .= "\n" . PartitionDefinition::build($this->partitions);
         }
         return 'CREATE ' . OptionsArray::build($this->options) . ' ' . Expression::build($this->name) . ' ' . $fields . OptionsArray::build($this->entityOptions) . $partition;
     } elseif ($this->options->has('VIEW')) {
         return 'CREATE ' . OptionsArray::build($this->options) . ' ' . Expression::build($this->name) . ' ' . $fields . ' AS ' . TokensList::build($this->body) . ' ' . OptionsArray::build($this->entityOptions);
     } elseif ($this->options->has('TRIGGER')) {
         return 'CREATE ' . OptionsArray::build($this->options) . ' ' . Expression::build($this->name) . ' ' . OptionsArray::build($this->entityOptions) . ' ' . 'ON ' . Expression::build($this->table) . ' ' . 'FOR EACH ROW ' . TokensList::build($this->body);
     } elseif ($this->options->has('PROCEDURE') || $this->options->has('FUNCTION')) {
         $tmp = '';
         if ($this->options->has('FUNCTION')) {
             $tmp = 'RETURNS ' . DataType::build($this->return);
         }
         return 'CREATE ' . OptionsArray::build($this->options) . ' ' . Expression::build($this->name) . ' ' . ParameterDefinition::build($this->parameters) . ' ' . $tmp . ' ' . TokensList::build($this->body);
     } else {
         return 'CREATE ' . OptionsArray::build($this->options) . ' ' . Expression::build($this->name) . ' ' . TokensList::build($this->body);
     }
     return '';
 }
Beispiel #4
0
 /**
  * @param AlterOperation $component The component to be built.
  * @param array          $options   Parameters for building.
  *
  * @return string
  */
 public static function build($component, array $options = array())
 {
     $ret = $component->options . ' ';
     if (isset($component->field) && $component->field !== '') {
         $ret .= $component->field . ' ';
     }
     $ret .= TokensList::build($component->unknown);
     return $ret;
 }
Beispiel #5
0
 /**
  * @param AlterOperation $component The component to be built.
  *
  * @return string
  */
 public static function build($component)
 {
     $ret = OptionsArray::build($component->options) . ' ';
     if (!empty($component->field)) {
         $ret .= Expression::build($component->field) . ' ';
     }
     $ret .= TokensList::build($component->unknown);
     return $ret;
 }
 /**
  * Jump to the end of the delimiter.
  *
  * @param Parser     $parser The instance that requests parsing.
  * @param TokensList $list   The list of tokens to be parsed.
  * @param Token      $token  The token that is being parsed.
  *
  * @return void
  */
 public function after(Parser $parser, TokensList $list, Token $token)
 {
     $list->getNextOfType(Token::TYPE_DELIMITER);
     --$list->idx;
 }
 /**
  * @param Parser     $parser  The parser that serves as context.
  * @param TokensList $list    The list of tokens that are being parsed.
  * @param array      $options Parameters for parsing.
  *
  * @return PartitionDefinition
  */
 public static function parse(Parser $parser, TokensList $list, array $options = array())
 {
     $ret = new PartitionDefinition();
     /**
      * The state of the parser.
      *
      * Below are the states of the parser.
      *
      *      0 -------------[ PARTITION | SUBPARTITION ]------------> 1
      *
      *      1 -----------------------[ name ]----------------------> 2
      *
      *      2 ----------------------[ VALUES ]---------------------> 3
      *
      *      3 ---------------------[ LESS THAN ]-------------------> 4
      *      3 ------------------------[ IN ]-----------------------> 4
      *
      *      4 -----------------------[ expr ]----------------------> 5
      *
      *      5 ----------------------[ options ]--------------------> 6
      *
      *      6 ------------------[ subpartitions ]------------------> (END)
      *
      * @var int $state
      */
     $state = 0;
     for (; $list->idx < $list->count; ++$list->idx) {
         /**
          * Token parsed at this moment.
          *
          * @var Token $token
          */
         $token = $list->tokens[$list->idx];
         // End of statement.
         if ($token->type === Token::TYPE_DELIMITER) {
             break;
         }
         // Skipping whitespaces and comments.
         if ($token->type === Token::TYPE_WHITESPACE || $token->type === Token::TYPE_COMMENT) {
             continue;
         }
         if ($state === 0) {
             $ret->isSubpartition = $token->type === Token::TYPE_KEYWORD && $token->value === 'SUBPARTITION';
             $state = 1;
         } elseif ($state === 1) {
             $ret->name = $token->value;
             // Looking ahead for a 'VALUES' keyword.
             $idx = $list->idx;
             $list->getNext();
             $nextToken = $list->getNext();
             $list->idx = $idx;
             $state = $nextToken->type === Token::TYPE_KEYWORD && $nextToken->value === 'VALUES' ? 2 : 5;
         } elseif ($state === 2) {
             $state = 3;
         } elseif ($state === 3) {
             $ret->type = $token->value;
             $state = 4;
         } elseif ($state === 4) {
             if ($token->value === 'MAXVALUE') {
                 $ret->expr = $token->value;
             } else {
                 $ret->expr = Expression::parse($parser, $list, array('parenthesesDelimited' => true, 'breakOnAlias' => true));
             }
             $state = 5;
         } elseif ($state === 5) {
             $ret->options = OptionsArray::parse($parser, $list, static::$OPTIONS);
             $state = 6;
         } elseif ($state === 6) {
             if ($token->type === Token::TYPE_OPERATOR && $token->value === '(') {
                 $ret->subpartitions = ArrayObj::parse($parser, $list, array('type' => 'SqlParser\\Components\\PartitionDefinition'));
                 ++$list->idx;
             }
             break;
         }
     }
     --$list->idx;
     return $ret;
 }
Beispiel #8
0
 public static function replaceTokens($list, array $find, array $replace)
 {
     /**
      * Whether the first parameter is a list.
      *
      * @var bool
      */
     $isList = $list instanceof TokensList;
     // Parsing the tokens.
     if (!$isList) {
         $list = Lexer::getTokens($list);
     }
     /**
      * The list to be returned.
      *
      * @var array
      */
     $newList = array();
     /**
      * The length of the find pattern is calculated only once.
      *
      * @var int
      */
     $findCount = count($find);
     /**
      * The starting index of the pattern.
      *
      * @var int
      */
     $i = 0;
     while ($i < $list->count) {
         // A sequence may not start with a comment.
         if ($list->tokens[$i]->type === Token::TYPE_COMMENT) {
             $newList[] = $list->tokens[$i];
             ++$i;
             continue;
         }
         /**
          * The index used to parse `$list->tokens`.
          *
          * This index might be running faster than `$k` because some tokens
          * are skipped.
          *
          * @var int
          */
         $j = $i;
         /**
          * The index used to parse `$find`.
          *
          * This index might be running slower than `$j` because some tokens
          * are skipped.
          *
          * @var int
          */
         $k = 0;
         // Checking if the next tokens match the pattern described.
         while ($j < $list->count && $k < $findCount) {
             // Comments are being skipped.
             if ($list->tokens[$j]->type === Token::TYPE_COMMENT) {
                 ++$j;
             }
             if (!static::match($list->tokens[$j], $find[$k])) {
                 // This token does not match the pattern.
                 break;
             }
             // Going to next token and segment of find pattern.
             ++$j;
             ++$k;
         }
         // Checking if the sequence was found.
         if ($k === $findCount) {
             // Inserting new tokens.
             foreach ($replace as $token) {
                 $newList[] = $token;
             }
             // Skipping next `$findCount` tokens.
             $i = $j;
         } else {
             // Adding the same token.
             $newList[] = $list->tokens[$i];
             ++$i;
         }
     }
     return $isList ? new TokensList($newList) : TokensList::build($newList);
 }
Beispiel #9
0
 /**
  * Parses the statements defined by the tokens list.
  *
  * @param Parser     $parser The instance that requests parsing.
  * @param TokensList $list   The list of tokens to be parsed.
  *
  * @return void
  */
 public function parse(Parser $parser, TokensList $list)
 {
     /**
      * Array containing all list of clauses parsed.
      * This is used to check for duplicates.
      *
      * @var array $parsedClauses
      */
     $parsedClauses = array();
     // This may be corrected by the parser.
     $this->first = $list->idx;
     /**
      * Whether options were parsed or not.
      * For statements that do not have any options this is set to `true` by
      * default.
      *
      * @var bool $parsedOptions
      */
     $parsedOptions = empty(static::$OPTIONS);
     for (; $list->idx < $list->count; ++$list->idx) {
         /**
          * Token parsed at this moment.
          *
          * @var Token $token
          */
         $token = $list->tokens[$list->idx];
         // End of statement.
         if ($token->type === Token::TYPE_DELIMITER) {
             break;
         }
         // Checking if this closing bracket is the pair for a bracket
         // outside the statement.
         if ($token->value === ')' && $parser->brackets > 0) {
             --$parser->brackets;
             continue;
         }
         // Only keywords are relevant here. Other parts of the query are
         // processed in the functions below.
         if ($token->type !== Token::TYPE_KEYWORD) {
             if ($token->type !== TOKEN::TYPE_COMMENT && $token->type !== Token::TYPE_WHITESPACE) {
                 $parser->error(__('Unexpected token.'), $token);
             }
             continue;
         }
         // Unions are parsed by the parser because they represent more than
         // one statement.
         if ($token->value === 'UNION' || $token->value === 'UNION ALL' || $token->value === 'UNION DISTINCT') {
             break;
         }
         $lastIdx = $list->idx;
         // ON DUPLICATE KEY UPDATE ...
         // has to be parsed in parent statement (INSERT or REPLACE)
         // so look for it and break
         if (get_class($this) === 'SqlParser\\Statements\\SelectStatement' && $token->value === 'ON') {
             ++$list->idx;
             // Skip ON
             // look for ON DUPLICATE KEY UPDATE
             $first = $list->getNextOfType(Token::TYPE_KEYWORD);
             $second = $list->getNextOfType(Token::TYPE_KEYWORD);
             $third = $list->getNextOfType(Token::TYPE_KEYWORD);
             if ($first && $second && $third && $first->value === 'DUPLICATE' && $second->value === 'KEY' && $third->value === 'UPDATE') {
                 $list->idx = $lastIdx;
                 break;
             }
         }
         $list->idx = $lastIdx;
         /**
          * The name of the class that is used for parsing.
          *
          * @var Component $class
          */
         $class = null;
         /**
          * The name of the field where the result of the parsing is stored.
          *
          * @var string $field
          */
         $field = null;
         /**
          * Parser's options.
          *
          * @var array $options
          */
         $options = array();
         // Looking for duplicated clauses.
         if (!empty(Parser::$KEYWORD_PARSERS[$token->value]) || !empty(Parser::$STATEMENT_PARSERS[$token->value])) {
             if (!empty($parsedClauses[$token->value])) {
                 $parser->error(__('This type of clause was previously parsed.'), $token);
                 break;
             }
             $parsedClauses[$token->value] = true;
         }
         // Checking if this is the beginning of a clause.
         if (!empty(Parser::$KEYWORD_PARSERS[$token->value])) {
             $class = Parser::$KEYWORD_PARSERS[$token->value]['class'];
             $field = Parser::$KEYWORD_PARSERS[$token->value]['field'];
             if (!empty(Parser::$KEYWORD_PARSERS[$token->value]['options'])) {
                 $options = Parser::$KEYWORD_PARSERS[$token->value]['options'];
             }
         }
         // Checking if this is the beginning of the statement.
         if (!empty(Parser::$STATEMENT_PARSERS[$token->value])) {
             if (!empty(static::$CLAUSES) && empty(static::$CLAUSES[$token->value])) {
                 // Some keywords (e.g. `SET`) may be the beginning of a
                 // statement and a clause.
                 // If such keyword was found and it cannot be a clause of
                 // this statement it means it is a new statement, but no
                 // delimiter was found between them.
                 $parser->error(__('A new statement was found, but no delimiter between it and the previous one.'), $token);
                 break;
             }
             if (!$parsedOptions) {
                 if (empty(static::$OPTIONS[$token->value])) {
                     // Skipping keyword because if it is not a option.
                     ++$list->idx;
                 }
                 $this->options = OptionsArray::parse($parser, $list, static::$OPTIONS);
                 $parsedOptions = true;
             }
         } elseif ($class === null) {
             // There is no parser for this keyword and isn't the beginning
             // of a statement (so no options) either.
             $parser->error(__('Unrecognized keyword.'), $token);
             continue;
         }
         $this->before($parser, $list, $token);
         // Parsing this keyword.
         if ($class !== null) {
             ++$list->idx;
             // Skipping keyword or last option.
             $this->{$field} = $class::parse($parser, $list, $options);
         }
         $this->after($parser, $list, $token);
     }
     // This may be corrected by the parser.
     $this->last = --$list->idx;
     // Go back to last used token.
 }
 public function testGetNextOfTypeAndValue()
 {
     $list = new TokensList($this->tokens);
     $this->assertEquals($this->tokens[0], $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'SELECT'));
     $this->assertEquals(null, $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'SELECT'));
 }