/** * @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 Expression[] */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = array(); /** * The state of the parser. * * Below are the states of the parser. * * 0 ----------------------[ array ]---------------------> 1 * * 1 ------------------------[ , ]------------------------> 0 * 1 -----------------------[ else ]----------------------> (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 ($token->type === Token::TYPE_KEYWORD && $token->flags & Token::FLAG_KEYWORD_RESERVED && ~$token->flags & Token::FLAG_KEYWORD_FUNCTION && $token->value !== 'DUAL' && $token->value !== 'NULL' && $token->value !== 'CASE') { // No keyword is expected. break; } if ($state === 0) { if ($token->type === Token::TYPE_KEYWORD && $token->value === 'CASE') { $expr = CaseExpression::parse($parser, $list, $options); } else { $expr = Expression::parse($parser, $list, $options); } if ($expr === null) { break; } $ret[] = $expr; $state = 1; } elseif ($state === 1) { if ($token->value === ',') { $state = 0; } else { break; } } } if ($state === 0) { $parser->error(__('An expression was expected.'), $list->tokens[$list->idx]); } --$list->idx; return $ret; }
/** * @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 OrderKeyword[] */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = array(); $expr = new OrderKeyword(); /** * The state of the parser. * * Below are the states of the parser. * * 0 ----------------------[ field ]----------------------> 1 * * 1 ------------------------[ , ]------------------------> 0 * 1 -------------------[ ASC / DESC ]--------------------> 1 * * @var int */ $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) { $expr->field = Expression::parse($parser, $list); $state = 1; } elseif ($state === 1) { if ($token->type === Token::TYPE_KEYWORD && ($token->value === 'ASC' || $token->value === 'DESC')) { $expr->type = $token->value; } elseif ($token->type === Token::TYPE_OPERATOR && $token->value === ',') { if (!empty($expr->field)) { $ret[] = $expr; } $expr = new OrderKeyword(); $state = 0; } else { break; } } } // Last iteration was not processed. if (!empty($expr->field)) { $ret[] = $expr; } --$list->idx; return $ret; }
/** * @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 Expression[] */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = array(); $expr = null; 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 ($token->type === Token::TYPE_KEYWORD && $token->flags & Token::FLAG_KEYWORD_RESERVED) { // No keyword is expected. break; } if ($token->type === Token::TYPE_OPERATOR && $token->value === ',') { $ret[] = $expr; } else { $expr = Expression::parse($parser, $list, $options); if ($expr === null) { break; } } } // Last iteration was not processed. if ($expr !== null) { $ret[] = $expr; } --$list->idx; return $ret; }
/** * @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 JoinKeyword[] */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = array(); $expr = new JoinKeyword(); /** * The state of the parser. * * Below are the states of the parser. * * 0 -----------------------[ JOIN ]----------------------> 1 * * 1 -----------------------[ expr ]----------------------> 2 * * 2 ------------------------[ ON ]-----------------------> 3 * 2 -----------------------[ USING ]---------------------> 4 * * 3 --------------------[ conditions ]-------------------> 0 * * 4 ----------------------[ columns ]--------------------> 0 * * @var int $state */ $state = 0; // By design, the parser will parse first token after the keyword. // In this case, the keyword must be analyzed too, in order to determine // the type of this join. if ($list->idx > 0) { --$list->idx; } 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) { if ($token->type === Token::TYPE_KEYWORD && !empty(static::$JOINS[$token->value])) { $expr->type = static::$JOINS[$token->value]; $state = 1; } else { break; } } elseif ($state === 1) { $expr->expr = Expression::parse($parser, $list, array('field' => 'table')); $state = 2; } elseif ($state === 2) { if ($token->type === Token::TYPE_KEYWORD) { if ($token->value === 'ON') { $state = 3; } elseif ($token->value === 'USING') { $state = 4; } } } elseif ($state === 3) { $expr->on = Condition::parse($parser, $list); $ret[] = $expr; $expr = new JoinKeyword(); $state = 0; } elseif ($state === 4) { $expr->using = ArrayObj::parse($parser, $list); $ret[] = $expr; $expr = new JoinKeyword(); $state = 0; } } if (!empty($expr->type)) { $ret[] = $expr; } --$list->idx; return $ret; }
/** * @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 AlterOperation */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = new AlterOperation(); /** * Counts brackets. * * @var int $brackets */ $brackets = 0; /** * The state of the parser. * * Below are the states of the parser. * * 0 ---------------------[ options ]---------------------> 1 * * 1 ----------------------[ field ]----------------------> 2 * * 2 -------------------------[ , ]-----------------------> 0 * * @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 comments. if ($token->type === Token::TYPE_COMMENT) { continue; } // Skipping whitespaces. if ($token->type === Token::TYPE_WHITESPACE) { if ($state === 2) { // When parsing the unknown part, the whitespaces are // included to not break anything. $ret->unknown[] = $token; } continue; } if ($state === 0) { $ret->options = OptionsArray::parse($parser, $list, static::$OPTIONS); $state = 1; } elseif ($state === 1) { $ret->field = Expression::parse($parser, $list, array('noAlias' => true, 'noBrackets' => true)); if ($ret->field === null) { // No field was read. We go back one token so the next // iteration will parse the same token, but in state 2. --$list->idx; } $state = 2; } elseif ($state === 2) { if ($token->type === Token::TYPE_OPERATOR) { if ($token->value === '(') { ++$brackets; } elseif ($token->value === ')') { --$brackets; } elseif ($token->value === ',' && $brackets === 0) { break; } } $ret->unknown[] = $token; } } if ($ret->options->isEmpty()) { $parser->error(__('Unrecognized alter operation.'), $list->tokens[$list->idx]); } --$list->idx; return $ret; }
/** * @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; }
/** * @param RenameOperation $component The component to be built. * * @return string */ public static function build($component) { if (is_array($component)) { $values = array(); foreach ($component as $c) { $values[] = static::build($c); } return implode(', ', $values); } else { return Expression::build($component->old) . ' TO ' . Expression::build($component->new); } }
/** * @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 IntoKeyword */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = new IntoKeyword(); /** * The state of the parser. * * Below are the states of the parser. * * 0 -----------------------[ name ]----------------------> 1 * 0 ---------------------[ OUTFILE ]---------------------> 2 * * 1 ------------------------[ ( ]------------------------> (END) * * 2 ---------------------[ filename ]--------------------> 1 * * @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 ($token->type === Token::TYPE_KEYWORD && $token->flags & Token::FLAG_KEYWORD_RESERVED) { if ($state === 0 && $token->value === 'OUTFILE') { $ret->type = 'OUTFILE'; $state = 2; continue; } // No other keyword is expected. break; } if ($state === 0) { $ret->dest = Expression::parse($parser, $list, array('noAlias' => true, 'noBrackets' => true, 'skipColumn' => true)); $state = 1; } elseif ($state === 1) { if ($token->type === Token::TYPE_OPERATOR && $token->value === '(') { $ret->columns = ArrayObj::parse($parser, $list)->values; ++$list->idx; } break; } elseif ($state === 2) { $ret->dest = $token->value; ++$list->idx; break; } } --$list->idx; return $ret; }
/** * @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) { ++$list->idx; // Skipping `CREATE`. // Parsing options. $this->options = OptionsArray::parse($parser, $list, static::$OPTIONS); ++$list->idx; // Skipping last option. // Parsing the field name. $this->name = Expression::parse($parser, $list, array('noAlias' => true, 'noBrackets' => true, 'skipColumn' => true)); if (!isset($this->name) || $this->name === '') { $parser->error(__('The name of the entity was expected.'), $list->tokens[$list->idx]); } else { ++$list->idx; // Skipping field. } if ($this->options->has('DATABASE')) { $this->entityOptions = OptionsArray::parse($parser, $list, static::$DB_OPTIONS); } elseif ($this->options->has('TABLE')) { $this->fields = CreateDefinition::parse($parser, $list); if (empty($this->fields)) { $parser->error(__('At least one column definition was expected.'), $list->tokens[$list->idx]); } ++$list->idx; $this->entityOptions = OptionsArray::parse($parser, $list, static::$TABLE_OPTIONS); /** * The field that is being filled (`partitionBy` or * `subpartitionBy`). * * @var string $field */ $field = null; /** * The number of brackets. `false` means no bracket was found * previously. At least one bracket is required to validate the * expression. * * @var int|bool $brackets */ $brackets = false; /* * Handles partitions. */ 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 comments. if ($token->type === Token::TYPE_COMMENT) { continue; } if ($token->type === Token::TYPE_KEYWORD && $token->value === 'PARTITION BY') { $field = 'partitionBy'; $brackets = false; } elseif ($token->type === Token::TYPE_KEYWORD && $token->value === 'SUBPARTITION BY') { $field = 'subpartitionBy'; $brackets = false; } elseif ($token->type === Token::TYPE_KEYWORD && $token->value === 'PARTITIONS') { $token = $list->getNextOfType(Token::TYPE_NUMBER); --$list->idx; // `getNextOfType` also advances one position. $this->partitionsNum = $token->value; } elseif ($token->type === Token::TYPE_KEYWORD && $token->value === 'SUBPARTITIONS') { $token = $list->getNextOfType(Token::TYPE_NUMBER); --$list->idx; // `getNextOfType` also advances one position. $this->subpartitionsNum = $token->value; } elseif (!empty($field)) { /* * Handling the content of `PARTITION BY` and `SUBPARTITION BY`. */ // Counting brackets. if ($token->type === Token::TYPE_OPERATOR && $token->value === '(') { // This is used instead of `++$brackets` because, // initially, `$brackets` is `false` cannot be // incremented. $brackets = $brackets + 1; } elseif ($token->type === Token::TYPE_OPERATOR && $token->value === ')') { --$brackets; } // Building the expression used for partitioning. $this->{$field} .= $token->type === Token::TYPE_WHITESPACE ? ' ' : $token->token; // Last bracket was read, the expression ended. // Comparing with `0` and not `false`, because `false` means // that no bracket was found and at least one must is // required. if ($brackets === 0) { $this->{$field} = trim($this->{$field}); $field = null; } } elseif ($token->type === Token::TYPE_OPERATOR && $token->value === '(') { if (!empty($this->partitionBy)) { $this->partitions = ArrayObj::parse($parser, $list, array('type' => 'SqlParser\\Components\\PartitionDefinition')); } break; } } } elseif ($this->options->has('PROCEDURE') || $this->options->has('FUNCTION')) { $this->parameters = ParameterDefinition::parse($parser, $list); if ($this->options->has('FUNCTION')) { $token = $list->getNextOfType(Token::TYPE_KEYWORD); if ($token->value !== 'RETURNS') { $parser->error(__('A "RETURNS" keyword was expected.'), $token); } else { ++$list->idx; $this->return = DataType::parse($parser, $list); } } ++$list->idx; $this->entityOptions = OptionsArray::parse($parser, $list, static::$FUNC_OPTIONS); ++$list->idx; for (; $list->idx < $list->count; ++$list->idx) { $token = $list->tokens[$list->idx]; $this->body[] = $token; } } elseif ($this->options->has('VIEW')) { $token = $list->getNext(); // Skipping whitespaces and comments. // Parsing columns list. if ($token->type === Token::TYPE_OPERATOR && $token->value === '(') { --$list->idx; // getNext() also goes forward one field. $this->fields = ArrayObj::parse($parser, $list); ++$list->idx; // Skipping last token from the array. $list->getNext(); } // Parsing the `AS` keyword. for (; $list->idx < $list->count; ++$list->idx) { $token = $list->tokens[$list->idx]; if ($token->type === Token::TYPE_DELIMITER) { break; } $this->body[] = $token; } } elseif ($this->options->has('TRIGGER')) { // Parsing the time and the event. $this->entityOptions = OptionsArray::parse($parser, $list, static::$TRIGGER_OPTIONS); ++$list->idx; $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'ON'); ++$list->idx; // Skipping `ON`. // Parsing the name of the table. $this->table = Expression::parse($parser, $list, array('noAlias' => true, 'noBrackets' => true, 'skipColumn' => true)); ++$list->idx; $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'FOR EACH ROW'); ++$list->idx; // Skipping `FOR EACH ROW`. for (; $list->idx < $list->count; ++$list->idx) { $token = $list->tokens[$list->idx]; $this->body[] = $token; } } else { for (; $list->idx < $list->count; ++$list->idx) { $token = $list->tokens[$list->idx]; if ($token->type === Token::TYPE_DELIMITER) { break; } $this->body[] = $token; } } }
/** * @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 RenameOperation[] */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = array(); $expr = new RenameOperation(); /** * The state of the parser. * * Below are the states of the parser. * * 0 ---------------------[ old name ]--------------------> 1 * * 1 ------------------------[ TO ]-----------------------> 2 * * 2 ---------------------[ old name ]--------------------> 3 * * 3 ------------------------[ , ]------------------------> 0 * 3 -----------------------[ else ]----------------------> (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) { $expr->old = Expression::parse($parser, $list, array('noAlias' => true, 'noBrackets' => true, 'skipColumn' => true)); if (empty($expr->old)) { $parser->error(__('The old name of the table was expected.'), $token); } $state = 1; } elseif ($state === 1) { if ($token->type === Token::TYPE_KEYWORD && $token->value === 'TO') { $state = 2; } else { $parser->error(__('Keyword "TO" was expected.'), $token); break; } } elseif ($state === 2) { $expr->new = Expression::parse($parser, $list, array('noBrackets' => true, 'skipColumn' => true, 'noAlias' => true)); if (empty($expr->new)) { $parser->error(__('The new name of the table was expected.'), $token); } $state = 3; } elseif ($state === 3) { if ($token->type === Token::TYPE_OPERATOR && $token->value === ',') { $ret[] = $expr; $expr = new RenameOperation(); $state = 0; } else { break; } } } if ($state !== 3) { $parser->error(__('A rename operation was expected.'), $list->tokens[$list->idx - 1]); } // Last iteration was not saved. if (!empty($expr->old)) { $ret[] = $expr; } --$list->idx; return $ret; }
/** * @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 AlterOperation */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = new AlterOperation(); /** * Counts brackets. * * @var int $brackets */ $brackets = 0; /** * The state of the parser. * * Below are the states of the parser. * * 0 ---------------------[ options ]---------------------> 1 * * 1 ----------------------[ field ]----------------------> 2 * * 2 -------------------------[ , ]-----------------------> 0 * * @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 comments. if ($token->type === Token::TYPE_COMMENT) { continue; } // Skipping whitespaces. if ($token->type === Token::TYPE_WHITESPACE) { if ($state === 2) { // When parsing the unknown part, the whitespaces are // included to not break anything. $ret->unknown[] = $token; } continue; } if ($state === 0) { $ret->options = OptionsArray::parse($parser, $list, $options); if ($ret->options->has('AS')) { for (; $list->idx < $list->count; ++$list->idx) { if ($list->tokens[$list->idx]->type === Token::TYPE_DELIMITER) { break; } $ret->unknown[] = $list->tokens[$list->idx]; } break; } $state = 1; } elseif ($state === 1) { $ret->field = Expression::parse($parser, $list, array('breakOnAlias' => true, 'parseField' => 'column')); if ($ret->field === null) { // No field was read. We go back one token so the next // iteration will parse the same token, but in state 2. --$list->idx; } $state = 2; } elseif ($state === 2) { if ($token->type === Token::TYPE_OPERATOR) { if ($token->value === '(') { ++$brackets; } elseif ($token->value === ')') { --$brackets; } elseif ($token->value === ',' && $brackets === 0) { break; } } elseif (!empty(Parser::$STATEMENT_PARSERS[$token->value])) { // We have reached the end of ALTER operation and suddenly found // a start to new statement, but have not find a delimiter between them if (!($token->value == 'SET' && $list->tokens[$list->idx - 1]->value == 'CHARACTER')) { $parser->error(__('A new statement was found, but no delimiter between it and the previous one.'), $token); break; } } $ret->unknown[] = $token; } } if ($ret->options->isEmpty()) { $parser->error(__('Unrecognized alter operation.'), $list->tokens[$list->idx]); } --$list->idx; return $ret; }
/** * @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 SetOperation[] */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = array(); $expr = new SetOperation(); /** * The state of the parser. * * Below are the states of the parser. * * 0 -------------------[ column name ]-------------------> 1 * * 1 ------------------------[ , ]------------------------> 0 * 1 ----------------------[ value ]----------------------> 1 * * @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; } // No keyword is expected. if ($token->type === Token::TYPE_KEYWORD && $token->flags & Token::FLAG_KEYWORD_RESERVED && $state == 0) { break; } if ($state === 0) { if ($token->token === '=') { $state = 1; } elseif ($token->value !== ',') { $expr->column .= $token->token; } } elseif ($state === 1) { $tmp = Expression::parse($parser, $list, array('breakOnAlias' => true)); if ($tmp == null) { break; } $expr->column = trim($expr->column); $expr->value = $tmp->expr; $ret[] = $expr; $expr = new SetOperation(); $state = 0; } } --$list->idx; return $ret; }
/** * @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 RenameOperation */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = array(); $expr = new RenameOperation(); /** * The state of the parser. * * Below are the states of the parser. * * 0 ---------------------[ old name ]--------------------> 1 * * 1 ------------------------[ TO ]-----------------------> 2 * * 2 ---------------------[ old name ]--------------------> 3 * * 3 ------------------------[ , ]------------------------> 0 * 3 -----------------------[ else ]----------------------> -1 * * @var int */ $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 ($token->type === Token::TYPE_KEYWORD && $token->flags & Token::FLAG_KEYWORD_RESERVED) { if ($state === 1 && $token->value === 'TO') { $state = 2; continue; } // No other keyword is expected. break; } if ($token->type === Token::TYPE_OPERATOR) { if ($state === 3 && $token->value === ',') { $ret[] = $expr; $expr = new RenameOperation(); $state = 0; continue; } // No other operator is expected. break; } if ($state == 0) { $expr->old = Expression::parse($parser, $list, array('noAlias' => true, 'noBrackets' => true, 'skipColumn' => true)); $state = 1; } elseif ($state == 2) { $expr->new = Expression::parse($parser, $list, array('noBrackets' => true, 'skipColumn' => true, 'noAlias' => true)); $state = 3; } } // Last iteration was not saved. if (!empty($expr->old)) { $ret[] = $expr; } --$list->idx; return $ret; }
/** * @param IntoKeyword $component The component to be built. * * @return string */ public static function build($component) { if ($component->dest instanceof Expression) { $columns = !empty($component->columns) ? '(' . implode(', ', $component->columns) . ')' : ''; return Expression::build($component->dest) . $columns; } else { return 'OUTFILE "' . $component->dest . '"'; } }
/** * @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 OptionsArray */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = new OptionsArray(); /** * The ID that will be assigned to duplicate options. * * @var int $lastAssignedId */ $lastAssignedId = count($options) + 1; /** * The option that was processed last time. * * @var array $lastOption */ $lastOption = null; /** * The index of the option that was processed last time. * * @var int $lastOptionId */ $lastOptionId = 0; /** * Counts brackets. * * @var int $brackets */ $brackets = 0; /** * The state of the parser. * * Below are the states of the parser. * * 0 ---------------------[ option ]----------------------> 1 * * 1 -------------------[ = (optional) ]------------------> 2 * * 2 ----------------------[ value ]----------------------> 0 * * @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 comments. if ($token->type === Token::TYPE_COMMENT) { continue; } // Skipping whitespace if not parsing value. if ($token->type === Token::TYPE_WHITESPACE && $brackets === 0) { continue; } if ($lastOption === null) { $upper = strtoupper($token->token); if (isset($options[$upper])) { $lastOption = $options[$upper]; $lastOptionId = is_array($lastOption) ? $lastOption[0] : $lastOption; $state = 0; // Checking for option conflicts. // For example, in `SELECT` statements the keywords `ALL` // and `DISTINCT` conflict and if used together, they // produce an invalid query. // // Usually, tokens can be identified in the array by the // option ID, but if conflicts occur, a generated option ID // is used. // // The first pseudo duplicate ID is the maximum value of the // real options (e.g. if there are 5 options, the first // fake ID is 6). if (isset($ret->options[$lastOptionId])) { $parser->error(sprintf(__('This option conflicts with "%1$s".'), is_array($ret->options[$lastOptionId]) ? $ret->options[$lastOptionId]['name'] : $ret->options[$lastOptionId]), $token); $lastOptionId = $lastAssignedId++; } } else { // There is no option to be processed. break; } } if ($state === 0) { if (!is_array($lastOption)) { // This is a just keyword option without any value. // This is the beginning and the end of it. $ret->options[$lastOptionId] = $token->value; $lastOption = null; $state = 0; } elseif ($lastOption[1] === 'var' || $lastOption[1] === 'var=') { // This is a keyword that is followed by a value. // This is only the beginning. The value is parsed in state // 1 and 2. State 1 is used to skip the first equals sign // and state 2 to parse the actual value. $ret->options[$lastOptionId] = array('name' => $token->value, 'equals' => $lastOption[1] === 'var=', 'expr' => '', 'value' => ''); $state = 1; } elseif ($lastOption[1] === 'expr' || $lastOption[1] === 'expr=') { // This is a keyword that is followed by an expression. // The expression is used by the specialized parser. // Skipping this option in order to parse the expression. ++$list->idx; $ret->options[$lastOptionId] = array('name' => $token->value, 'equals' => $lastOption[1] === 'expr=', 'expr' => ''); $state = 1; } } elseif ($state === 1) { $state = 2; if ($token->token === '=') { $ret->options[$lastOptionId]['equals'] = true; continue; } } // This is outside the `elseif` group above because the change might // change this iteration. if ($state === 2) { if ($lastOption[1] === 'expr' || $lastOption[1] === 'expr=') { $ret->options[$lastOptionId]['expr'] = Expression::parse($parser, $list, empty($lastOption[2]) ? array() : $lastOption[2]); $ret->options[$lastOptionId]['value'] = $ret->options[$lastOptionId]['expr']->expr; $lastOption = null; $state = 0; } else { if ($token->token === '(') { ++$brackets; } elseif ($token->token === ')') { --$brackets; } $ret->options[$lastOptionId]['expr'] .= $token->token; if (!($token->token === '(' && $brackets === 1 || $token->token === ')' && $brackets === 0)) { // First pair of brackets is being skipped. $ret->options[$lastOptionId]['value'] .= $token->value; } // Checking if we finished parsing. if ($brackets === 0) { $lastOption = null; } } } } /* * We reached the end of statement without getting a value * for an option for which a value was required */ if ($state === 1 && $lastOption && ($lastOption[1] == 'expr' || $lastOption[1] == 'var' || $lastOption[1] == 'var=' || $lastOption[1] == 'expr=')) { $parser->error(sprintf(__('Value/Expression for the option %1$s was expected'), $ret->options[$lastOptionId]['name']), $list->tokens[$list->idx - 1]); } if (empty($options['_UNSORTED'])) { ksort($ret->options); } --$list->idx; return $ret; }
/** * @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; $state = $ret->isSubpartition ? 5 : 2; } 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('bracketsDelimited' => true, 'noAlias' => 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; }
/** * @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) { ++$list->idx; // Skipping `CREATE`. // Parsing options. $this->options = OptionsArray::parse($parser, $list, static::$OPTIONS); ++$list->idx; // Skipping last option. // Parsing the field name. $this->name = Expression::parse($parser, $list, array('noAlias' => true, 'noBrackets' => true, 'skipColumn' => true)); ++$list->idx; // Skipping field. if ($this->options->has('DATABASE')) { $this->entityOptions = OptionsArray::parse($parser, $list, static::$DB_OPTIONS); } elseif ($this->options->has('TABLE')) { $this->fields = FieldDefinition::parse($parser, $list); ++$list->idx; $this->entityOptions = OptionsArray::parse($parser, $list, static::$TABLE_OPTIONS); } elseif ($this->options->has('PROCEDURE') || $this->options->has('FUNCTION')) { $this->parameters = ParameterDefinition::parse($parser, $list); if ($this->options->has('FUNCTION')) { $token = $list->getNextOfType(Token::TYPE_KEYWORD); if ($token->value !== 'RETURNS') { $parser->error('\'RETURNS\' keyword was expected.', $token); } else { ++$list->idx; $this->return = DataType::parse($parser, $list); } } ++$list->idx; $this->entityOptions = OptionsArray::parse($parser, $list, static::$FUNC_OPTIONS); ++$list->idx; for (; $list->idx < $list->count; ++$list->idx) { $token = $list->tokens[$list->idx]; $this->body[] = $token; } } else { if ($this->options->has('VIEW')) { $token = $list->getNext(); // Skipping whitespaces and comments. // Parsing columns list. if ($token->type === Token::TYPE_OPERATOR && $token->value === '(') { --$list->idx; // getNext() also goes forward one field. $this->fields = ArrayObj::parse($parser, $list); ++$list->idx; // Skipping last token from the array. $list->getNext(); } // Parsing the `AS` keyword. for (; $list->idx < $list->count; ++$list->idx) { $token = $list->tokens[$list->idx]; if ($token->type === Token::TYPE_DELIMITER) { break; } $this->body[] = $token; } } else { if ($this->options->has('TRIGGER')) { // Parsing the time and the event. $this->entityOptions = OptionsArray::parse($parser, $list, static::$TRIGGER_OPTIONS); ++$list->idx; $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'ON'); ++$list->idx; // Skipping `ON`. // Parsing the name of the table. $this->table = Expression::parse($parser, $list, array('noAlias' => true, 'noBrackets' => true, 'skipColumn' => true)); ++$list->idx; $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'FOR EACH ROW'); ++$list->idx; // Skipping `FOR EACH ROW`. for (; $list->idx < $list->count; ++$list->idx) { $token = $list->tokens[$list->idx]; $this->body[] = $token; } } } } }
/** * @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 Reference */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = new Reference(); /** * The state of the parser. * * Below are the states of the parser. * * 0 ----------------------[ table ]---------------------> 1 * * 1 ---------------------[ columns ]--------------------> 2 * * 2 ---------------------[ options ]--------------------> (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->table = Expression::parse($parser, $list, array('parseField' => 'table', 'breakOnAlias' => true)); $state = 1; } elseif ($state === 1) { $ret->columns = ArrayObj::parse($parser, $list)->values; $state = 2; } elseif ($state === 2) { $ret->options = OptionsArray::parse($parser, $list, static::$REFERENCES_OPTIONS); ++$list->idx; break; } } --$list->idx; return $ret; }
public function testBuild() { $component = array(new Expression('1 + 2', 'three'), new Expression('1 + 3', 'four')); $this->assertEquals(Expression::build($component), '1 + 2 AS `three`, 1 + 3 AS `four`'); }
/** * @param JoinKeyword[] $component The component to be built. * * @return string */ public static function build($component) { $ret = array(); foreach ($component as $c) { $ret[] = ($c->type === 'JOIN' ? 'JOIN ' : $c->type . ' JOIN ') . Expression::build($c->expr) . ' ON ' . Condition::build($c->on); } return implode(' ', $ret); }
/** * @param OrderKeyword $component The component to be built. * * @return string */ public static function build($component) { if (is_array($component)) { $ret = array(); foreach ($component as $c) { $ret[] = static::build($c); } return implode(", ", $ret); } else { return Expression::build($component->field) . ' ' . $component->type; } }
/** * @return string */ public function build() { $tmp = array(); foreach ($this->altered as $altered) { $tmp[] = $altered::build($altered); } return 'ALTER ' . OptionsArray::build($this->options) . ' TABLE ' . Expression::build($this->table) . ' ' . implode(', ', $tmp); }
/** * Gets a list of all tables used in this statement. * * @param Statement $statement Statement to be scanned. * * @return array */ public static function getTables($statement) { $expressions = array(); if ($statement instanceof InsertStatement || $statement instanceof ReplaceStatement) { $expressions = array($statement->into->dest); } elseif ($statement instanceof UpdateStatement) { $expressions = $statement->tables; } elseif ($statement instanceof SelectStatement || $statement instanceof DeleteStatement) { $expressions = $statement->from; } elseif ($statement instanceof AlterStatement || $statement instanceof TruncateStatement) { $expressions = array($statement->table); } elseif ($statement instanceof DropStatement) { if (!$statement->options->has('TABLE')) { // No tables are dropped. return array(); } $expressions = $statement->fields; } elseif ($statement instanceof RenameStatement) { foreach ($statement->renames as $rename) { $expressions[] = $rename->old; } } $ret = array(); foreach ($expressions as $expr) { if (!empty($expr->table)) { $expr->expr = null; // Force rebuild. $expr->alias = null; // Aliases are not required. $ret[] = Expression::build($expr); } } return $ret; }
/** * * @param Parser $parser The parser that serves as context. * @param TokensList $list The list of tokens that are being parsed. * * @return Expression */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = new CaseExpression(); /** * State of parser * * @var int $parser */ $state = 0; /** * Syntax type (type 0 or type 1) * * @var int $type */ $type = 0; ++$list->idx; // Skip 'CASE' for (; $list->idx < $list->count; ++$list->idx) { /** * Token parsed at this moment. * * @var Token $token */ $token = $list->tokens[$list->idx]; // Skipping whitespaces and comments. if ($token->type === Token::TYPE_WHITESPACE || $token->type === Token::TYPE_COMMENT) { continue; } if ($state === 0) { if ($token->type === Token::TYPE_KEYWORD && $token->value === 'WHEN') { ++$list->idx; // Skip 'WHEN' $new_condition = Condition::parse($parser, $list); $type = 1; $state = 1; $ret->conditions[] = $new_condition; } elseif ($token->type === Token::TYPE_KEYWORD && $token->value === 'ELSE') { ++$list->idx; // Skip 'ELSE' $ret->else_result = Expression::parse($parser, $list); $state = 0; // last clause of CASE expression } elseif ($token->type === Token::TYPE_KEYWORD && ($token->value === 'END' || $token->value === 'end')) { $state = 3; // end of CASE expression ++$list->idx; break; } elseif ($token->type === Token::TYPE_KEYWORD) { $parser->error(__('Unexpected keyword.'), $token); break; } else { $ret->value = Expression::parse($parser, $list); $type = 0; $state = 1; } } elseif ($state === 1) { if ($type === 0) { if ($token->type === Token::TYPE_KEYWORD && $token->value === 'WHEN') { ++$list->idx; // Skip 'WHEN' $new_value = Expression::parse($parser, $list); $state = 2; $ret->compare_values[] = $new_value; } elseif ($token->type === Token::TYPE_KEYWORD && $token->value === 'ELSE') { ++$list->idx; // Skip 'ELSE' $ret->else_result = Expression::parse($parser, $list); $state = 0; // last clause of CASE expression } elseif ($token->type === Token::TYPE_KEYWORD && ($token->value === 'END' || $token->value === 'end')) { $state = 3; // end of CASE expression ++$list->idx; break; } elseif ($token->type === Token::TYPE_KEYWORD) { $parser->error(__('Unexpected keyword.'), $token); break; } } else { if ($token->type === Token::TYPE_KEYWORD && $token->value === 'THEN') { ++$list->idx; // Skip 'THEN' $new_result = Expression::parse($parser, $list); $state = 0; $ret->results[] = $new_result; } elseif ($token->type === Token::TYPE_KEYWORD) { $parser->error(__('Unexpected keyword.'), $token); break; } } } elseif ($state === 2) { if ($type === 0) { if ($token->type === Token::TYPE_KEYWORD && $token->value === 'THEN') { ++$list->idx; // Skip 'THEN' $new_result = Expression::parse($parser, $list); $ret->results[] = $new_result; $state = 1; } elseif ($token->type === Token::TYPE_KEYWORD) { $parser->error(__('Unexpected keyword.'), $token); break; } } } } if ($state !== 3) { $parser->error(__('Unexpected end of CASE expression'), $list->tokens[$list->idx - 1]); } --$list->idx; return $ret; }