Initial commit

This commit is contained in:
2020-10-07 10:37:15 +02:00
commit ce5f440392
28157 changed files with 4429172 additions and 0 deletions

View File

@@ -0,0 +1,223 @@
<?php
/**
* AbstractProcessor.php
*
* This file implements an abstract processor, which implements some helper functions.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/../utils/ExpressionType.php';
require_once dirname(__FILE__) . '/../lexer/PHPSQLLexer.php';
/**
*
* This class processes contains some general functions for a processor.
*
* @author arothe
*
*/
abstract class AbstractProcessor {
/**
* This function implements the main functionality of a processor class.
* Always use default valuses for additional parameters within overridden functions.
*/
abstract public function process($tokens);
/**
* this function splits up a SQL statement into easy to "parse"
* tokens for the SQL processor
*/
public function splitSQLIntoTokens($sql) {
$lexer = new PHPSQLLexer();
return $lexer->split($sql);
}
/**
* Revokes the quoting characters from an expression
*/
protected function revokeQuotation($sql) {
$result = trim($sql);
if (($result[0] === '`') && ($result[strlen($result) - 1] === '`')) {
$result = substr($result, 1, -1);
return trim(str_replace('``', '`', $result));
}
if (($result[0] === "'") && ($result[strlen($result) - 1] === "'")) {
$result = substr($result, 1, -1);
return trim(str_replace("''", "'", $result));
}
if (($result[0] === "\"") && ($result[strlen($result) - 1] === "\"")) {
$result = substr($result, 1, -1);
return trim(str_replace("\"\"", "\"", $result));
}
return $sql;
}
/**
* This method removes parenthesis from start of the given string.
* It removes also the associated closing parenthesis.
*/
protected function removeParenthesisFromStart($token) {
$parenthesisRemoved = 0;
$trim = trim($token);
if ($trim !== "" && $trim[0] === "(") { // remove only one parenthesis pair now!
$parenthesisRemoved++;
$trim[0] = " ";
$trim = trim($trim);
}
$parenthesis = $parenthesisRemoved;
$i = 0;
$string = 0;
while ($i < strlen($trim)) {
if ($trim[$i] === "\\") {
$i += 2; // an escape character, the next character is irrelevant
continue;
}
if ($trim[$i] === "'" || $trim[$i] === '"') {
$string++;
}
if (($string % 2 === 0) && ($trim[$i] === "(")) {
$parenthesis++;
}
if (($string % 2 === 0) && ($trim[$i] === ")")) {
if ($parenthesis == $parenthesisRemoved) {
$trim[$i] = " ";
$parenthesisRemoved--;
}
$parenthesis--;
}
$i++;
}
return trim($trim);
}
protected function getVariableType($expression) {
// $expression must contain only upper-case characters
if ($expression[1] !== "@") {
return ExpressionType::USER_VARIABLE;
}
$type = substr($expression, 2, strpos($expression, ".", 2));
switch ($type) {
case 'GLOBAL':
$type = ExpressionType::GLOBAL_VARIABLE;
break;
case 'LOCAL':
$type = ExpressionType::LOCAL_VARIABLE;
break;
case 'SESSION':
default:
$type = ExpressionType::SESSION_VARIABLE;
break;
}
return $type;
}
protected function isCommaToken($token) {
return trim($token) === ",";
}
protected function isWhitespaceToken($token) {
return trim($token) === "";
}
protected function isCommentToken($token) {
return isset($token[0], $token[1])
&& (($token[0] === '-' && $token[1] === '-') || ($token[0] === '/' && $token[1] === '*'));
}
protected function isColumnReference($out) {
return isset($out['expr_type']) && $out['expr_type'] === ExpressionType::COLREF;
}
protected function isReserved($out) {
return isset($out['expr_type']) && $out['expr_type'] === ExpressionType::RESERVED;
}
protected function isConstant($out) {
return isset($out['expr_type']) && $out['expr_type'] === ExpressionType::CONSTANT;
}
protected function isAggregateFunction($out) {
return isset($out['expr_type']) && $out['expr_type'] === ExpressionType::AGGREGATE_FUNCTION;
}
protected function isFunction($out) {
return isset($out['expr_type']) && $out['expr_type'] === ExpressionType::SIMPLE_FUNCTION;
}
protected function isExpression($out) {
return isset($out['expr_type']) && $out['expr_type'] === ExpressionType::EXPRESSION;
}
protected function isBracketExpression($out) {
return isset($out['expr_type']) && $out['expr_type'] === ExpressionType::BRACKET_EXPRESSION;
}
protected function isSubQuery($out) {
return isset($out['expr_type']) && $out['expr_type'] === ExpressionType::SUBQUERY;
}
/**
* translates an array of objects into an associative array
*/
public function toArray($tokenList) {
$expr = array();
foreach ($tokenList as $token) {
$expr[] = $token->toArray();
}
return empty($expr) ? false : $expr;
}
protected function array_insert_after($array, $key, $entry) {
$idx = array_search($key, array_keys($array));
$array = array_slice($array, 0, $idx + 1, true) + $entry
+ array_slice($array, $idx + 1, count($array) - 1, true);
return $array;
}
}

View File

@@ -0,0 +1,496 @@
<?php
/**
* ColumnDefinitionProcessor.php
*
* This file implements the processor for column definition part of a CREATE TABLE statement.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/ReferenceDefinitionProcessor.php';
require_once dirname(__FILE__) . '/ExpressionListProcessor.php';
require_once dirname(__FILE__) . '/../utils/ExpressionType.php';
/**
*
* This class processes the column definition part of a CREATE TABLE statement.
*
* @author arothe
*
*/
class ColumnDefinitionProcessor extends AbstractProcessor {
protected function processExpressionList($parsed) {
$processor = new ExpressionListProcessor();
return $processor->process($parsed);
}
protected function processReferenceDefinition($parsed) {
$processor = new ReferenceDefinitionProcessor();
return $processor->process($parsed);
}
protected function removeComma($tokens) {
$res = array();
foreach ($tokens as $token) {
if (trim($token) !== ',') {
$res[] = $token;
}
}
return $res;
}
protected function buildColDef($expr, $base_expr, $options, $refs, $key) {
$expr = array('expr_type' => ExpressionType::COLUMN_TYPE, 'base_expr' => $base_expr, 'sub_tree' => $expr);
// add options first
$expr['sub_tree'] = array_merge($expr['sub_tree'], $options['sub_tree']);
unset($options['sub_tree']);
$expr = array_merge($expr, $options);
// followed by references
if (count($refs) !== 0) {
$expr['sub_tree'] = array_merge($expr['sub_tree'], $refs);
}
$expr['till'] = $key;
return $expr;
}
public function process($tokens) {
$trim = '';
$base_expr = '';
$currCategory = '';
$expr = array();
$refs = array();
$options = array('unique' => false, 'nullable' => true, 'auto_inc' => false, 'primary' => false,
'sub_tree' => array(),
);
$skip = 0;
foreach ($tokens as $key => $token) {
$trim = trim($token);
$base_expr .= $token;
if ($skip > 0) {
$skip--;
continue;
}
if ($skip < 0) {
break;
}
if ($trim === '') {
continue;
}
$upper = strtoupper($trim);
switch ($upper) {
case ',':
// we stop on a single comma and return
// the $expr entry and the index $key
$expr = $this->buildColDef(
$expr,
trim(substr($base_expr, 0, -strlen($token))),
$options,
$refs,
$key - 1
);
break 2;
case 'VARCHAR':
$expr[] = array('expr_type' => ExpressionType::DATA_TYPE, 'base_expr' => $trim, 'length' => false);
$prevCategory = 'TEXT';
$currCategory = 'SINGLE_PARAM_PARENTHESIS';
continue 2;
case 'VARBINARY':
$expr[] = array('expr_type' => ExpressionType::DATA_TYPE, 'base_expr' => $trim, 'length' => false);
$prevCategory = $upper;
$currCategory = 'SINGLE_PARAM_PARENTHESIS';
continue 2;
case 'UNSIGNED':
$last = array_pop($expr);
$last['unsigned'] = true;
$expr[] = $last;
continue 2;
case 'ZEROFILL':
$last = array_pop($expr);
$last['zerofill'] = true;
$expr[] = $last;
continue 2;
case 'BIT':
case 'TINYBIT':
case 'SMALLINT':
case 'MEDIUMINT':
case 'INT':
case 'INTEGER':
case 'BIGINT':
$expr[] = array('expr_type' => ExpressionType::DATA_TYPE, 'base_expr' => $trim, 'unsigned' => false,
'zerofill' => false, 'length' => false,
);
$currCategory = 'SINGLE_PARAM_PARENTHESIS';
$prevCategory = $upper;
continue 2;
case 'BINARY':
if ($currCategory === 'TEXT') {
$last = array_pop($expr);
$last['binary'] = true;
$last['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$expr[] = $last;
continue 2;
}
$expr[] = array('expr_type' => ExpressionType::DATA_TYPE, 'base_expr' => $trim, 'length' => false);
$currCategory = 'SINGLE_PARAM_PARENTHESIS';
$prevCategory = $upper;
continue 2;
case 'CHAR':
$expr[] = array('expr_type' => ExpressionType::DATA_TYPE, 'base_expr' => $trim, 'length' => false);
$currCategory = 'SINGLE_PARAM_PARENTHESIS';
$prevCategory = 'TEXT';
continue 2;
case 'REAL':
case 'DOUBLE':
case 'FLOAT':
$expr[] = array('expr_type' => ExpressionType::DATA_TYPE, 'base_expr' => $trim, 'unsigned' => false,
'zerofill' => false,
);
$currCategory = 'TWO_PARAM_PARENTHESIS';
$prevCategory = $upper;
continue 2;
case 'DECIMAL':
case 'NUMERIC':
$expr[] = array('expr_type' => ExpressionType::DATA_TYPE, 'base_expr' => $trim, 'unsigned' => false,
'zerofill' => false,
);
$currCategory = 'TWO_PARAM_PARENTHESIS';
$prevCategory = $upper;
continue 2;
case 'DATE':
case 'TIME':
case 'TIMESTAMP':
case 'DATETIME':
case 'YEAR':
case 'TINYBLOB':
case 'BLOB':
case 'MEDIUMBLOB':
case 'LONGBLOB':
$expr[] = array('expr_type' => ExpressionType::DATA_TYPE, 'base_expr' => $trim);
$prevCategory = $currCategory = $upper;
continue 2;
// the next token can be BINARY
case 'TINYTEXT':
case 'TEXT':
case 'MEDIUMTEXT':
case 'LONGTEXT':
$prevCategory = $currCategory = 'TEXT';
$expr[] = array('expr_type' => ExpressionType::DATA_TYPE, 'base_expr' => $trim, 'binary' => false);
continue 2;
case 'ENUM':
case 'SET':
$currCategory = 'MULTIPLE_PARAM_PARENTHESIS';
$prevCategory = 'TEXT';
$expr[] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim, 'sub_tree' => false);
continue 2;
case 'GEOMETRY':
case 'POINT':
case 'LINESTRING':
case 'POLYGON':
case 'MULTIPOINT':
case 'MULTILINESTRING':
case 'MULTIPOLYGON':
case 'GEOMETRYCOLLECTION':
$expr[] = array('expr_type' => ExpressionType::DATA_TYPE, 'base_expr' => $trim);
$prevCategory = $currCategory = $upper;
// TODO: is it right?
// spatial types
continue 2;
case 'CHARACTER':
if ($prevCategory === 'TEXT') {
$parsed = array('expr_type' => ExpressionType::DATA_TYPE, 'base_expr' => $trim);
$expr[] = array('expr_type' => ExpressionType::CHARSET, 'base_expr' => substr($base_expr, 0, -1),
'sub_tree' => $parsed,
);
$base_expr = $token;
$currCategory = 'CHARSET';
continue 2;
}
// else ?
break;
case 'SET':
if ($currCategory === 'CHARSET') {
// TODO: is it necessary to set special properties like the name or collation?
$parsed = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$last = array_pop($expr);
$last['sub_tree'][] = $parsed;
$expr[] = $last;
continue 2;
}
// else ?
break;
case 'COLLATE':
if ($prevCategory === 'TEXT') {
$parsed = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$expr[] = array('expr_type' => ExpressionType::COLLATE, 'base_expr' => substr($base_expr, 0, -1),
'sub_tree' => $parsed,
);
$base_expr = $token;
$currCategory = 'COLLATION';
continue 2;
}
// else ?
break;
case 'NOT':
case 'NULL':
$options['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
if ($options['nullable']) {
$options['nullable'] = ($upper === 'NOT' ? false : true);
}
continue 2;
case 'DEFAULT':
case 'COMMENT':
$currCategory = $upper;
$options['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
continue 2;
case 'AUTO_INCREMENT':
$options['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$options['auto_inc'] = true;
continue 2;
case 'COLUMN_FORMAT':
case 'STORAGE':
$currCategory = $upper;
$options['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
continue 2;
case 'UNIQUE':
// it can follow a KEY word
$currCategory = $upper;
$options['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$options['unique'] = true;
continue 2;
case 'PRIMARY':
// it must follow a KEY word
$options['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
continue 2;
case 'KEY':
$options['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
if ($currCategory !== 'UNIQUE') {
$options['primary'] = true;
}
continue 2;
case 'REFERENCES':
$refs = $this->processReferenceDefinition(array_splice($tokens, $key - 1, null, true));
$skip = $refs['till'] - $key;
unset($refs['till']);
// TODO: check this, we need the last comma
continue 2;
default:
switch ($currCategory) {
case 'STORAGE':
if ($upper === 'DISK' || $upper === 'MEMORY' || $upper === 'DEFAULT') {
$options['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$options['storage'] = $trim;
continue 3;
}
// else ?
break;
case 'COLUMN_FORMAT':
if ($upper === 'FIXED' || $upper === 'DYNAMIC' || $upper === 'DEFAULT') {
$options['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$options['col_format'] = $trim;
continue 3;
}
// else ?
break;
case 'COMMENT':
// this is the comment string
$options['sub_tree'][] = array('expr_type' => ExpressionType::COMMENT, 'base_expr' => $trim);
$options['comment'] = $trim;
$currCategory = $prevCategory;
break;
case 'DEFAULT':
// this is the default value
$options['sub_tree'][] = array('expr_type' => ExpressionType::DEF_VALUE, 'base_expr' => $trim);
$options['default'] = $trim;
$currCategory = $prevCategory;
break;
case 'COLLATE':
// this is the collation name
$parsed = array('expr_type' => ExpressionType::CONSTANT, 'base_expr' => $trim);
$last = array_pop($expr);
$last['sub_tree'][] = $parsed;
$t = $base_expr;
$base_expr = $last['base_expr'] . $base_expr;
$last['base_expr'] = $t;
$currCategory = $prevCategory;
break;
case 'CHARSET':
// this is the character set name
$parsed = array('expr_type' => ExpressionType::CONSTANT, 'base_expr' => $trim);
$last = array_pop($expr);
$last['sub_tree'][] = $parsed;
$t = $base_expr;
$base_expr = $last['base_expr'] . $base_expr;
$last['base_expr'] = $t;
$currCategory = $prevCategory;
break;
case 'SINGLE_PARAM_PARENTHESIS':
$parsed = $this->removeParenthesisFromStart($trim);
$parsed = array('expr_type' => ExpressionType::CONSTANT, 'base_expr' => trim($parsed));
$last = array_pop($expr);
$last['length'] = $parsed['base_expr'];
$expr[] = $last;
$expr[] = array('expr_type' => ExpressionType::BRACKET_EXPRESSION, 'base_expr' => $trim,
'sub_tree' => array($parsed),
);
$currCategory = $prevCategory;
break;
case 'TWO_PARAM_PARENTHESIS':
// maximum of two parameters
$parsed = $this->removeParenthesisFromStart($trim);
$parsed = $this->splitSQLIntoTokens($parsed);
$parsed = $this->removeComma($parsed);
$parsed = $this->processExpressionList($parsed);
// TODO: check that
$last = array_pop($expr);
$last['length'] = $parsed[0]['base_expr'];
$last['decimals'] = isset($parsed[1]) ? $parsed[1]['base_expr'] : false;
$expr[] = $last;
$expr[] = array('expr_type' => ExpressionType::BRACKET_EXPRESSION, 'base_expr' => $trim,
'sub_tree' => $parsed,
);
$currCategory = $prevCategory;
break;
case 'MULTIPLE_PARAM_PARENTHESIS':
// some parameters
$parsed = $this->removeParenthesisFromStart($trim);
$parsed = $this->splitSQLIntoTokens($parsed);
$parsed = $this->removeComma($parsed);
$this->processExpressionList($parsed);
$last = array_pop($expr);
$last['sub_tree'] = array('expr_type' => ExpressionType::BRACKET_EXPRESSION, 'base_expr' => $trim,
'sub_tree' => $parsed,
);
$expr[] = $last;
$currCategory = $prevCategory;
break;
default:
break;
}
}
$prevCategory = $currCategory;
$currCategory = '';
}
if (!isset($expr['till'])) {
// end of $tokens array
$expr = $this->buildColDef($expr, trim($base_expr), $options, $refs, -1);
}
return $expr;
}
}

View File

@@ -0,0 +1,55 @@
<?php
/**
* ColumnListProcessor.php
*
* This file implements the processor for column lists like in INSERT statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/../utils/ExpressionType.php';
/**
*
* This class processes column-lists.
*
* @author arothe
*
*/
class ColumnListProcessor extends AbstractProcessor {
public function process($tokens) {
$columns = explode(",", $tokens);
$cols = array();
foreach ($columns as $k => $v) {
$cols[] = array('expr_type' => ExpressionType::COLREF, 'base_expr' => trim($v),
'no_quotes' => $this->revokeQuotation($v),
);
}
return $cols;
}
}

View File

@@ -0,0 +1,449 @@
<?php
/**
* CreateDefinitionProcessor.php
*
* This file implements the processor for the create definition within the TABLE statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/ColumnDefinitionProcessor.php';
require_once dirname(__FILE__) . '/IndexColumnListProcessor.php';
require_once dirname(__FILE__) . '/ReferenceDefinitionProcessor.php';
require_once dirname(__FILE__) . '/../utils/ExpressionType.php';
/**
*
* This class processes the create definition of the TABLE statements.
*
* @author arothe
*
*/
class CreateDefinitionProcessor extends AbstractProcessor {
protected function correctExpressionType(&$expr) {
$type = ExpressionType::EXPRESSION;
if (!isset($expr[0]) || !isset($expr[0]['expr_type'])) {
return $type;
}
// replace the constraint type with a more descriptive one
switch ($expr[0]['expr_type']) {
case ExpressionType::CONSTRAINT:
$type = $expr[1]['expr_type'];
$expr[1]['expr_type'] = ExpressionType::RESERVED;
break;
case ExpressionType::COLREF:
$type = ExpressionType::COLDEF;
break;
default:
$type = $expr[0]['expr_type'];
$expr[0]['expr_type'] = ExpressionType::RESERVED;
break;
}
return $type;
}
public function process($tokens) {
$base_expr = "";
$prevCategory = "";
$currCategory = "";
$expr = array();
$result = array();
$skip = 0;
foreach ($tokens as $k => $token) {
$trim = trim($token);
$base_expr .= $token;
if ($skip !== 0) {
$skip--;
continue;
}
if ($trim === "") {
continue;
}
$upper = strtoupper($trim);
switch ($upper) {
case 'CONSTRAINT':
$expr[] = array('expr_type' => ExpressionType::CONSTRAINT, 'base_expr' => $trim, 'sub_tree' => false);
$currCategory = $prevCategory = $upper;
continue 2;
case 'LIKE':
$expr[] = array('expr_type' => ExpressionType::LIKE, 'base_expr' => $trim);
$currCategory = $prevCategory = $upper;
continue 2;
case 'FOREIGN':
if ($prevCategory === "" || $prevCategory === "CONSTRAINT") {
$expr[] = array('expr_type' => ExpressionType::FOREIGN_KEY, 'base_expr' => $trim);
$currCategory = $upper;
continue 2;
}
// else ?
break;
case 'PRIMARY':
if ($prevCategory === "" || $prevCategory === "CONSTRAINT") {
// next one is KEY
$expr[] = array('expr_type' => ExpressionType::PRIMARY_KEY, 'base_expr' => $trim);
$currCategory = $upper;
continue 2;
}
// else ?
break;
case 'UNIQUE':
if ($prevCategory === "" || $prevCategory === "CONSTRAINT") {
// next one is KEY
$expr[] = array('expr_type' => ExpressionType::UNIQUE_IDX, 'base_expr' => $trim);
$currCategory = $upper;
continue 2;
}
// else ?
break;
case 'KEY':
// the next one is an index name
if ($currCategory === 'PRIMARY' || $currCategory === 'FOREIGN' || $currCategory === 'UNIQUE') {
$expr[] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
continue 2;
}
$expr[] = array('expr_type' => ExpressionType::INDEX, 'base_expr' => $trim);
$currCategory = $upper;
continue 2;
case 'CHECK':
$expr[] = array('expr_type' => ExpressionType::CHECK, 'base_expr' => $trim);
$currCategory = $upper;
continue 2;
case 'INDEX':
if ($currCategory === 'UNIQUE' || $currCategory === 'FULLTEXT' || $currCategory === 'SPATIAL') {
$expr[] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
continue 2;
}
$expr[] = array('expr_type' => ExpressionType::INDEX, 'base_expr' => $trim);
$currCategory = $upper;
continue 2;
case 'FULLTEXT':
$expr[] = array('expr_type' => ExpressionType::FULLTEXT_IDX, 'base_expr' => $trim);
$currCategory = $prevCategory = $upper;
continue 2;
case 'SPATIAL':
$expr[] = array('expr_type' => ExpressionType::SPATIAL_IDX, 'base_expr' => $trim);
$currCategory = $prevCategory = $upper;
continue 2;
case 'WITH':
// starts an index option
if ($currCategory === 'INDEX_COL_LIST') {
$option = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$expr[] = array('expr_type' => ExpressionType::INDEX_PARSER,
'base_expr' => substr($base_expr, 0, -strlen($token)),
'sub_tree' => array($option),
);
$base_expr = $token;
$currCategory = 'INDEX_PARSER';
continue 2;
}
break;
case 'KEY_BLOCK_SIZE':
// starts an index option
if ($currCategory === 'INDEX_COL_LIST') {
$option = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$expr[] = array('expr_type' => ExpressionType::INDEX_SIZE,
'base_expr' => substr($base_expr, 0, -strlen($token)),
'sub_tree' => array($option),
);
$base_expr = $token;
$currCategory = 'INDEX_SIZE';
continue 2;
}
break;
case 'USING':
// starts an index option
if ($currCategory === 'INDEX_COL_LIST' || $currCategory === 'PRIMARY') {
$option = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$expr[] = array('base_expr' => substr($base_expr, 0, -strlen($token)), 'trim' => $trim,
'category' => $currCategory, 'sub_tree' => array($option),
);
$base_expr = $token;
$currCategory = 'INDEX_TYPE';
continue 2;
}
// else ?
break;
case 'REFERENCES':
if ($currCategory === 'INDEX_COL_LIST' && $prevCategory === 'FOREIGN') {
$processor = new ReferenceDefinitionProcessor();
$refs = $processor->process(array_slice($tokens, $k - 1, null, true));
$skip = $refs['till'] - $k;
unset($refs['till']);
$expr[] = $refs;
$currCategory = $upper;
}
break;
case 'BTREE':
case 'HASH':
if ($currCategory === 'INDEX_TYPE') {
$last = array_pop($expr);
$last['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$expr[] = array('expr_type' => ExpressionType::INDEX_TYPE, 'base_expr' => $base_expr,
'sub_tree' => $last['sub_tree'],
);
$base_expr = $last['base_expr'] . $base_expr;
// FIXME: it could be wrong for index_type within index_option
$currCategory = $last['category'];
continue 2;
}
//else
break;
case '=':
if ($currCategory === 'INDEX_SIZE') {
// the optional character between KEY_BLOCK_SIZE and the numeric constant
$last = array_pop($expr);
$last['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$expr[] = $last;
continue 2;
}
break;
case 'PARSER':
if ($currCategory === 'INDEX_PARSER') {
$last = array_pop($expr);
$last['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$expr[] = $last;
continue 2;
}
// else?
break;
case ',':
// this starts the next definition
$type = $this->correctExpressionType($expr);
$result['create-def'][] = array('expr_type' => $type,
'base_expr' => trim(substr($base_expr, 0, -strlen($token))),
'sub_tree' => $expr,
);
$base_expr = "";
$expr = array();
break;
default:
switch ($currCategory) {
case 'LIKE':
// this is the tablename after LIKE
$expr[] = array('expr_type' => ExpressionType::TABLE, 'table' => $trim, 'base_expr' => $trim,
'no_quotes' => $this->revokeQuotation($trim),
);
break;
case 'PRIMARY':
if ($upper[0] === '(' && substr($upper, -1) === ')') {
// the column list
$processor = new IndexColumnListProcessor();
$cols = $processor->process($this->removeParenthesisFromStart($trim));
$expr[] = array('expr_type' => ExpressionType::COLUMN_LIST, 'base_expr' => $trim,
'sub_tree' => $cols,
);
$prevCategory = $currCategory;
$currCategory = "INDEX_COL_LIST";
continue 3;
}
// else?
break;
case 'FOREIGN':
if ($upper[0] === '(' && substr($upper, -1) === ')') {
$processor = new IndexColumnListProcessor();
$cols = $processor->process($this->removeParenthesisFromStart($trim));
$expr[] = array('expr_type' => ExpressionType::COLUMN_LIST, 'base_expr' => $trim,
'sub_tree' => $cols,
);
$prevCategory = $currCategory;
$currCategory = "INDEX_COL_LIST";
continue 3;
}
// index name
$expr[] = array('expr_type' => ExpressionType::CONSTANT, 'base_expr' => $trim);
continue 3;
case 'KEY':
case 'UNIQUE':
case 'INDEX':
if ($upper[0] === '(' && substr($upper, -1) === ')') {
$processor = new IndexColumnListProcessor();
$cols = $processor->process($this->removeParenthesisFromStart($trim));
$expr[] = array('expr_type' => ExpressionType::COLUMN_LIST, 'base_expr' => $trim,
'sub_tree' => $cols,
);
$prevCategory = $currCategory;
$currCategory = "INDEX_COL_LIST";
continue 3;
}
// index name
$expr[] = array('expr_type' => ExpressionType::CONSTANT, 'base_expr' => $trim);
continue 3;
case 'CONSTRAINT':
// constraint name
$last = array_pop($expr);
$last['base_expr'] = $base_expr;
$last['sub_tree'] = array('expr_type' => ExpressionType::CONSTANT, 'base_expr' => $trim);
$expr[] = $last;
continue 3;
case 'INDEX_PARSER':
// index parser name
$last = array_pop($expr);
$last['sub_tree'][] = array('expr_type' => ExpressionType::CONSTANT, 'base_expr' => $trim);
$expr[] = array('expr_type' => ExpressionType::INDEX_PARSER, 'base_expr' => $base_expr,
'sub_tree' => $last['sub_tree'],
);
$base_expr = $last['base_expr'] . $base_expr;
$currCategory = 'INDEX_COL_LIST';
continue 3;
case 'INDEX_SIZE':
// index key block size numeric constant
$last = array_pop($expr);
$last['sub_tree'][] = array('expr_type' => ExpressionType::CONSTANT, 'base_expr' => $trim);
$expr[] = array('expr_type' => ExpressionType::INDEX_SIZE, 'base_expr' => $base_expr,
'sub_tree' => $last['sub_tree'],
);
$base_expr = $last['base_expr'] . $base_expr;
$currCategory = 'INDEX_COL_LIST';
continue 3;
case 'CHECK':
if ($upper[0] === '(' && substr($upper, -1) === ')') {
$processor = new ExpressionListProcessor();
$unparsed = $this->splitSQLIntoTokens($this->removeParenthesisFromStart($trim));
$parsed = $processor->process($unparsed);
$expr[] = array('expr_type' => ExpressionType::BRACKET_EXPRESSION, 'base_expr' => $trim,
'sub_tree' => $parsed,
);
}
// else?
break;
case '':
// if the currCategory is empty, we have an unknown token,
// which is a column reference
$expr[] = array('expr_type' => ExpressionType::COLREF, 'base_expr' => $trim,
'no_quotes' => $this->revokeQuotation($trim),
);
$currCategory = 'COLUMN_NAME';
continue 3;
case 'COLUMN_NAME':
// the column-definition
// it stops on a comma or on a parenthesis
$processor = new ColumnDefinitionProcessor();
$parsed = $processor->process(array_slice($tokens, $k, null, true), $expr);
$skip = $parsed['till'] - $k;
unset($parsed['till']);
$expr[] = $parsed;
$currCategory = '';
break;
default:
// ?
break;
}
break;
}
$prevCategory = $currCategory;
$currCategory = '';
}
$type = $this->correctExpressionType($expr);
$result['create-def'][] = array('expr_type' => $type, 'base_expr' => trim($base_expr), 'sub_tree' => $expr);
return $result;
}
}

View File

@@ -0,0 +1,97 @@
<?php
/**
* CreateProcessor.php
*
* This file implements the processor for the CREATE TABLE statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/../utils/ExpressionType.php';
/**
*
* This class processes the CREATE statements.
*
* @author arothe
*
*/
class CreateProcessor extends AbstractProcessor {
public function process($tokens) {
$result = array();
$base_expr = "";
foreach ($tokens as $token) {
$trim = strtoupper(trim($token));
$base_expr .= $token;
if ($trim === "") {
continue;
}
switch ($trim) {
case 'TEMPORARY':
$result['expr_type'] = ExpressionType::TEMPORARY_TABLE;
$result['not-exists'] = false;
$expr[] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
break;
case 'TABLE':
$result['expr_type'] = ExpressionType::TABLE;
$result['not-exists'] = false;
$expr[] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
break;
case 'IF':
$expr[] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
break;
case 'NOT':
$expr[] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
break;
case 'EXISTS':
$result['not-exists'] = true;
$expr[] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
break;
default:
break;
}
}
$result['base_expr'] = trim($base_expr);
$result['sub_tree'] = $expr;
return $result;
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* DefaultProcessor.php
*
* This file implements the processor the unparsed sql string given by the user.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/UnionProcessor.php';
require_once dirname(__FILE__) . '/SQLProcessor.php';
/**
*
* This class processes the incoming sql string.
*
* @author arothe
*
*/
class DefaultProcessor extends AbstractProcessor {
public function process($sql) {
$inputArray = $this->splitSQLIntoTokens($sql);
// this is the highest level lexical analysis. This is the part of the
// code which finds UNION and UNION ALL query parts
$processor = new UnionProcessor();
$queries = $processor->process($inputArray);
// If there was no UNION or UNION ALL in the query, then the query is
// stored at $queries[0].
if (!$processor->isUnion($queries)) {
$processor = new SQLProcessor();
$queries = $processor->process($queries[0]);
}
return $queries;
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* DeleteProcessor.php
*
* This file implements the processor for the DELETE statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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';
/**
*
* This class processes the DELETE statements.
*
* @author arothe
*
*/
class DeleteProcessor extends AbstractProcessor {
public function process($tokens) {
$tables = array();
$del = $tokens['DELETE'];
foreach ($tokens['DELETE'] as $expression) {
if ($expression !== 'DELETE' && trim($expression, ' .*') !== "" && !$this->isCommaToken($expression)) {
$tables[] = trim($expression, '.* ');
}
}
if (empty($tables)) {
foreach ($tokens['FROM'] as $table) {
$tables[] = $table['table'];
}
}
$tokens['DELETE'] = array('TABLES' => $tables);
return $tokens;
}
}

View File

@@ -0,0 +1,46 @@
<?php
/**
* DescProcessor.php
*
* This file implements the processor for the DESC statements, which is a short form of DESCRIBE.
*
* Copyright (c) 2010-2014, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/ExplainProcessor.php';
/**
*
* This class processes the DESC statement.
*
* @author arothe
*
*/
class DescProcessor extends ExplainProcessor {
protected function isStatement($keys, $needle = "DESC") {
return parent::isStatement($keys, $needle);
}
}

View File

@@ -0,0 +1,46 @@
<?php
/**
* DescribeProcessor.php
*
* This file implements the processor for the DESCRIBE statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/ExplainProcessor.php';
/**
*
* This class processes the DESCRIBE statements.
*
* @author arothe
*
*/
class DescribeProcessor extends ExplainProcessor {
protected function isStatement($keys, $needle = "DESCRIBE") {
return parent::isStatement($keys, $needle);
}
}

View File

@@ -0,0 +1,112 @@
<?php
/**
* DropProcessor.php
*
* This file implements the processor for the DROP statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/../utils/ExpressionToken.php';
require_once dirname(__FILE__) . '/../utils/ExpressionType.php';
require_once dirname(__FILE__) . '/AbstractProcessor.php';
/**
*
* This class processes the DROP statements.
*
* @author arothe
*
*/
class DropProcessor extends AbstractProcessor {
// TODO: we should enhance it to get the positions for the IF EXISTS keywords
// look into the CreateProcessor to get an idea.
public function process($tokenList) {
$skip = 0;
$warning = true;
$base_expr = "";
$expr_type = false;
$option = false;
$resultList = array();
foreach ($tokenList as $k => $v) {
$token = new ExpressionToken($k, $v);
if ($token->isWhitespaceToken()) {
continue;
}
if ($skip > 0) {
$skip--;
continue;
}
switch ($token->getUpper()) {
case 'VIEW':
case 'SCHEMA':
case 'DATABASE':
case 'TABLE':
$expr_type = strtolower($token->getTrim());
break;
case 'IF':
$warning = false;
$skip = 1;
break;
case 'TEMPORARY':
$expr_type = ExpressionType::TEMPORARY_TABLE;
$skip = 1;
break;
case 'RESTRICT':
case 'CASCADE':
$option = $token->getUpper();
break;
case ',':
$resultList[] = array('expr_type' => $expr_type, 'base_expr' => $base_expr);
$base_expr = "";
break;
default:
$base_expr .= $token->getToken();
}
}
if ($base_expr !== "") {
$resultList[] = array('expr_type' => $expr_type, 'base_expr' => $base_expr);
}
return array('option' => $option, 'warning' => $warning, 'object_list' => $resultList);
}
}

View File

@@ -0,0 +1,46 @@
<?php
/**
* DuplicateProcessor.php
*
* This file implements the processor for the DUPLICATE statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/SetProcessor.php';
/**
*
* This class processes the DUPLICATE statements.
*
* @author arothe
*
*/
class DuplicateProcessor extends SetProcessor {
public function process($tokens) {
return parent::process($tokens, false);
}
}

View File

@@ -0,0 +1,145 @@
<?php
/**
* ExplainProcessor.php
*
* This file implements the processor for the EXPLAIN statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/../utils/ExpressionType.php';
/**
*
* This class processes the EXPLAIN statements.
*
* @author arothe
*
*/
class ExplainProcessor extends AbstractProcessor {
protected function isStatement($keys, $needle = "EXPLAIN") {
$pos = array_search($needle, $keys);
if (isset($keys[$pos + 1])) {
return in_array($keys[$pos + 1], array('SELECT', 'DELETE', 'INSERT', 'REPLACE', 'UPDATE'), true);
}
return false;
}
// TODO: refactor that function
public function process($tokens, $keys = array()) {
$base_expr = "";
$expr = array();
$currCategory = "";
if ($this->isStatement($keys)) {
foreach ($tokens as $token) {
$trim = trim($token);
$base_expr .= $token;
if ($trim === '') {
continue;
}
$upper = strtoupper($trim);
switch ($upper) {
case 'EXTENDED':
case 'PARTITIONS':
return array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $token);
break;
case 'FORMAT':
if ($currCategory === '') {
$currCategory = $upper;
$expr[] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
}
// else?
break;
case '=':
if ($currCategory === 'FORMAT') {
$expr[] = array('expr_type' => ExpressionType::OPERATOR, 'base_expr' => $trim);
}
// else?
break;
case 'TRADITIONAL':
case 'JSON':
if ($currCategory === 'FORMAT') {
$expr[] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
return array('expr_type' => ExpressionType::EXPRESSION, 'base_expr' => trim($base_expr),
'sub_tree' => $expr,
);
}
// else?
break;
default:
// ignore the other stuff
break;
}
}
return empty($expr) ? null : $expr;
}
foreach ($tokens as $token) {
$trim = trim($token);
if ($trim === '') {
continue;
}
switch ($currCategory) {
case 'TABLENAME':
$currCategory = 'WILD';
$expr[] = array('expr_type' => ExpressionType::COLREF, 'base_expr' => $trim);
break;
case '':
$currCategory = 'TABLENAME';
$expr[] = array('expr_type' => ExpressionType::TABLE, 'base_expr' => $trim);
break;
default:
break;
}
}
return empty($expr) ? null : $expr;
}
}

View File

@@ -0,0 +1,400 @@
<?php
/**
* ExpressionListProcessor.php
*
* This file implements the processor for expression lists.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/DefaultProcessor.php';
require_once dirname(__FILE__) . '/../utils/ExpressionToken.php';
require_once dirname(__FILE__) . '/../utils/ExpressionType.php';
/**
*
* This class processes expression lists.
*
* @author arothe
*
*/
class ExpressionListProcessor extends AbstractProcessor {
public function process($tokens) {
$resultList = array();
$skip_next = false;
$prev = new ExpressionToken();
foreach ($tokens as $k => $v) {
$curr = new ExpressionToken($k, $v);
if ($curr->isWhitespaceToken()) {
continue;
}
if ($skip_next) {
// skip the next non-whitespace token
$skip_next = false;
continue;
}
/* is it a subquery? */
if ($curr->isSubQueryToken()) {
$processor = new DefaultProcessor();
$curr->setSubTree($processor->process($this->removeParenthesisFromStart($curr->getTrim())));
$curr->setTokenType(ExpressionType::SUBQUERY);
} elseif ($curr->isEnclosedWithinParenthesis()) {
/* is it an in-list? */
$localTokenList = $this->splitSQLIntoTokens($this->removeParenthesisFromStart($curr->getTrim()));
if ($prev->getUpper() === 'IN') {
foreach ($localTokenList as $k => $v) {
$tmpToken = new ExpressionToken($k, $v);
if ($tmpToken->isCommaToken()) {
unset($localTokenList[$k]);
}
}
$localTokenList = array_values($localTokenList);
$curr->setSubTree($this->process($localTokenList));
$curr->setTokenType(ExpressionType::IN_LIST);
} elseif ($prev->getUpper() === 'AGAINST') {
$match_mode = false;
foreach ($localTokenList as $k => $v) {
$tmpToken = new ExpressionToken($k, $v);
switch ($tmpToken->getUpper()) {
case 'WITH':
$match_mode = 'WITH QUERY EXPANSION';
break;
case 'IN':
$match_mode = 'IN BOOLEAN MODE';
break;
default:
}
if ($match_mode !== false) {
unset($localTokenList[$k]);
}
}
$tmpToken = $this->process($localTokenList);
if ($match_mode !== false) {
$match_mode = new ExpressionToken(0, $match_mode);
$match_mode->setTokenType(ExpressionType::MATCH_MODE);
$tmpToken[] = $match_mode->toArray();
}
$curr->setSubTree($tmpToken);
$curr->setTokenType(ExpressionType::MATCH_ARGUMENTS);
$prev->setTokenType(ExpressionType::SIMPLE_FUNCTION);
} elseif ($prev->isColumnReference() || $prev->isFunction() || $prev->isAggregateFunction()) {
// if we have a colref followed by a parenthesis pair,
// it isn't a colref, it is a user-function
// TODO: this should be a method, because we need the same code
// below for unspecified tokens (expressions).
$localExpr = new ExpressionToken();
$tmpExprList = array();
foreach ($localTokenList as $k => $v) {
$tmpToken = new ExpressionToken($k, $v);
if (!$tmpToken->isCommaToken()) {
$localExpr->addToken($v);
$tmpExprList[] = $v;
} else {
// an expression could have multiple parts split by operands
// if we have a comma, it is a split-point for expressions
$tmpExprList = array_values($tmpExprList);
$localExprList = $this->process($tmpExprList);
if (count($localExprList) > 1) {
$localExpr->setSubTree($localExprList);
$localExpr->setTokenType(ExpressionType::EXPRESSION);
$localExprList = $localExpr->toArray();
$localExprList['alias'] = false;
$localExprList = array($localExprList);
}
if (!$curr->getSubTree()) {
$curr->setSubTree($localExprList);
} else {
$tmpExprList = $curr->getSubTree();
$curr->setSubTree(array_merge($tmpExprList, $localExprList));
}
$tmpExprList = array();
$localExpr = new ExpressionToken();
}
}
$tmpExprList = array_values($tmpExprList);
$localExprList = $this->process($tmpExprList);
if (count($localExprList) > 1) {
$localExpr->setSubTree($localExprList);
$localExpr->setTokenType(ExpressionType::EXPRESSION);
$localExprList = $localExpr->toArray();
$localExprList['alias'] = false;
$localExprList = array($localExprList);
}
if (!$curr->getSubTree()) {
$curr->setSubTree($localExprList);
} else {
$tmpExprList = $curr->getSubTree();
$curr->setSubTree(array_merge($tmpExprList, $localExprList));
}
$prev->setSubTree($curr->getSubTree());
if ($prev->isColumnReference()) {
$prev->setTokenType(ExpressionType::SIMPLE_FUNCTION);
$prev->setNoQuotes(null);
}
array_pop($resultList);
$curr = $prev;
}
// we have parenthesis, but it seems to be an expression
if ($curr->isUnspecified()) {
// TODO: the localTokenList could contain commas and further expressions,
// we must handle that like function parameters (see above)!
// this should solve issue 51
$curr->setSubTree($this->process($localTokenList));
$curr->setTokenType(ExpressionType::BRACKET_EXPRESSION);
}
} elseif ($curr->isVariableToken()) {
// a variable
// it can be quoted
$curr->setTokenType($this->getVariableType($curr->getUpper()));
$curr->setSubTree(false);
$curr->setNoQuotes(trim(trim($curr->getToken()), '@'), "`'\"");
} else {
/* it is either an operator, a colref or a constant */
switch ($curr->getUpper()) {
case '*':
$curr->setSubTree(false); // o subtree
// single or first element of expression list -> all-column-alias
if (empty($resultList)) {
$curr->setTokenType(ExpressionType::COLREF);
break;
}
// if the last token is colref, const or expression
// then * is an operator
// but if the previous colref ends with a dot, the * is the all-columns-alias
if (!$prev->isColumnReference() && !$prev->isConstant() && !$prev->isExpression()
&& !$prev->isBracketExpression() && !$prev->isAggregateFunction() && !$prev->isVariable()) {
$curr->setTokenType(ExpressionType::COLREF);
break;
}
if ($prev->isColumnReference() && $prev->endsWith(".")) {
$prev->addToken('*'); // tablealias dot *
continue 2; // skip the current token
}
$curr->setTokenType(ExpressionType::OPERATOR);
break;
case ':=':
case 'AND':
case '&&':
case 'BETWEEN':
case 'AND':
case 'BINARY':
case '&':
case '~':
case '|':
case '^':
case 'DIV':
case '/':
case '<=>':
case '=':
case '>=':
case '>':
case 'IS':
case 'NOT':
case '<<':
case '<=':
case '<':
case 'LIKE':
case '%':
case '!=':
case '<>':
case 'REGEXP':
case '!':
case '||':
case 'OR':
case '>>':
case 'RLIKE':
case 'SOUNDS':
case 'XOR':
case 'IN':
$curr->setSubTree(false);
$curr->setTokenType(ExpressionType::OPERATOR);
break;
case 'NULL':
$curr->setSubTree(false);
$curr->setTokenType(ExpressionType::CONSTANT);
break;
case '-':
case '+':
// differ between preceding sign and operator
$curr->setSubTree(false);
if ($prev->isColumnReference() || $prev->isFunction() || $prev->isAggregateFunction()
|| $prev->isConstant() || $prev->isSubQuery() || $prev->isExpression()
|| $prev->isBracketExpression() || $prev->isVariable()) {
$curr->setTokenType(ExpressionType::OPERATOR);
} else {
$curr->setTokenType(ExpressionType::SIGN);
}
break;
default:
$curr->setSubTree(false);
switch ($curr->getToken(0)) {
case "'":
case '"':
// it is a string literal
$curr->setTokenType(ExpressionType::CONSTANT);
break;
case '`':
// it is an escaped colum name
$curr->setTokenType(ExpressionType::COLREF);
$curr->setNoQuotes($curr->getToken());
break;
default:
if (is_numeric($curr->getToken())) {
if ($prev->isSign()) {
$prev->addToken($curr->getToken()); // it is a negative numeric constant
$prev->setTokenType(ExpressionType::CONSTANT);
continue 3;
// skip current token
} else {
$curr->setTokenType(ExpressionType::CONSTANT);
}
} else {
$curr->setTokenType(ExpressionType::COLREF);
$curr->setNoQuotes($curr->getToken());
}
break;
}
}
}
/* is a reserved word? */
if (!$curr->isOperator() && !$curr->isInList() && !$curr->isFunction() && !$curr->isAggregateFunction()
&& PHPSQLParserConstants::isReserved($curr->getUpper())) {
if (PHPSQLParserConstants::isAggregateFunction($curr->getUpper())) {
$curr->setTokenType(ExpressionType::AGGREGATE_FUNCTION);
$curr->setNoQuotes(null);
} elseif ($curr->getUpper() === 'NULL') {
// it is a reserved word, but we would like to set it as constant
$curr->setTokenType(ExpressionType::CONSTANT);
} else {
if (PHPSQLParserConstants::isParameterizedFunction($curr->getUpper())) {
// issue 60: check functions with parameters
// -> colref (we check parameters later)
// -> if there is no parameter, we leave the colref
$curr->setTokenType(ExpressionType::COLREF);
} elseif (PHPSQLParserConstants::isFunction($curr->getUpper())) {
$curr->setTokenType(ExpressionType::SIMPLE_FUNCTION);
$curr->setNoQuotes(null);
} else {
$curr->setTokenType(ExpressionType::RESERVED);
$curr->setNoQuotes(null);
}
}
}
// issue 94, INTERVAL 1 MONTH
if ($curr->isConstant() && PHPSQLParserConstants::isParameterizedFunction($prev->getUpper())) {
$prev->setTokenType(ExpressionType::RESERVED);
$prev->setNoQuotes(null);
}
if ($prev->isConstant() && PHPSQLParserConstants::isParameterizedFunction($curr->getUpper())) {
$curr->setTokenType(ExpressionType::RESERVED);
$curr->setNoQuotes(null);
}
if ($curr->isUnspecified()) {
$curr->setTokenType(ExpressionType::EXPRESSION);
$curr->setNoQuotes(null);
$curr->setSubTree($this->process($this->splitSQLIntoTokens($curr->getTrim())));
}
$resultList[] = $curr;
$prev = $curr;
} // end of for-loop
return $this->toArray($resultList);
}
}

View File

@@ -0,0 +1,249 @@
<?php
/**
* FromProcessor.php
*
* This file implements the processor for the FROM statements.
*
* Copyright (c) 2010-2013, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/DefaultProcessor.php';
require_once dirname(__FILE__) . '/../utils/ExpressionType.php';
/**
*
* This class processes the FROM statements.
*
* @author arothe
*
*/
class FromProcessor extends AbstractProcessor {
protected function initParseInfo($parseInfo = false) {
// first init
if ($parseInfo === false) {
$parseInfo = array('join_type' => "", 'saved_join_type' => "JOIN");
}
// loop init
return array('expression' => "", 'token_count' => 0, 'table' => "", 'no_quotes' => "", 'alias' => false,
'join_type' => "", 'next_join_type' => "", 'saved_join_type' => $parseInfo['saved_join_type'],
'ref_type' => false, 'ref_expr' => false, 'base_expr' => false, 'sub_tree' => false,
'subquery' => "",
);
}
protected function processFromExpression(&$parseInfo) {
$res = array();
// exchange the join types (join_type is save now, saved_join_type holds the next one)
$parseInfo['join_type'] = $parseInfo['saved_join_type']; // initialized with JOIN
$parseInfo['saved_join_type'] = ($parseInfo['next_join_type'] ? $parseInfo['next_join_type'] : 'JOIN');
// we have a reg_expr, so we have to parse it
if ($parseInfo['ref_expr'] !== false) {
$unparsed = $this->splitSQLIntoTokens($this->removeParenthesisFromStart($parseInfo['ref_expr']));
// here we can get a comma separated list
foreach ($unparsed as $k => $v) {
if ($this->isCommaToken($v)) {
$unparsed[$k] = "";
}
}
$processor = new ExpressionListProcessor();
$parseInfo['ref_expr'] = $processor->process($unparsed);
}
// there is an expression, we have to parse it
if (substr(trim($parseInfo['table']), 0, 1) == '(') {
$parseInfo['expression'] = $this->removeParenthesisFromStart($parseInfo['table']);
if (preg_match("/^\\s*select/i", $parseInfo['expression'])) {
$processor = new DefaultProcessor();
$parseInfo['sub_tree'] = $processor->process($parseInfo['expression']);
$res['expr_type'] = ExpressionType::SUBQUERY;
} else {
$tmp = $this->splitSQLIntoTokens($parseInfo['expression']);
$parseInfo['sub_tree'] = $this->process($tmp);
$res['expr_type'] = ExpressionType::TABLE_EXPRESSION;
}
} else {
$res['expr_type'] = ExpressionType::TABLE;
$res['table'] = $parseInfo['table'];
$res['no_quotes'] = $this->revokeQuotation($parseInfo['table']);
}
$res['alias'] = $parseInfo['alias'];
$res['join_type'] = $parseInfo['join_type'];
$res['ref_type'] = $parseInfo['ref_type'];
$res['ref_clause'] = $parseInfo['ref_expr'];
$res['base_expr'] = trim($parseInfo['expression']);
$res['sub_tree'] = $parseInfo['sub_tree'];
return $res;
}
public function process($tokens) {
$parseInfo = $this->initParseInfo();
$expr = array();
$skip_next = false;
$i = 0;
foreach ($tokens as $token) {
$upper = strtoupper(trim($token));
if ($skip_next && $token !== "") {
$parseInfo['token_count']++;
$skip_next = false;
continue;
} else {
if ($skip_next) {
continue;
}
}
switch ($upper) {
case 'OUTER':
case 'LEFT':
case 'RIGHT':
case 'NATURAL':
case 'CROSS':
case ',':
case 'JOIN':
case 'INNER':
break;
default:
$parseInfo['expression'] .= $token;
if ($parseInfo['ref_type'] !== false) { // all after ON / USING
$parseInfo['ref_expr'] .= $token;
}
break;
}
switch ($upper) {
case 'AS':
$parseInfo['alias'] = array('as' => true, 'name' => "", 'base_expr' => $token);
$parseInfo['token_count']++;
$n = 1;
$str = "";
while ($str == "") {
$parseInfo['alias']['base_expr'] .= ($tokens[$i + $n] === "" ? " " : $tokens[$i + $n]);
$str = trim($tokens[$i + $n]);
++$n;
}
$parseInfo['alias']['name'] = $str;
$parseInfo['alias']['no_quotes'] = $this->revokeQuotation($str);
$parseInfo['alias']['base_expr'] = trim($parseInfo['alias']['base_expr']);
continue;
case 'INDEX':
if ($token_category == 'CREATE') {
$token_category = $upper;
continue 2;
}
break;
case 'USING':
case 'ON':
$parseInfo['ref_type'] = $upper;
$parseInfo['ref_expr'] = "";
case 'CROSS':
case 'USE':
case 'FORCE':
case 'IGNORE':
case 'INNER':
case 'OUTER':
$parseInfo['token_count']++;
continue;
break;
case 'FOR':
$parseInfo['token_count']++;
$skip_next = true;
continue;
break;
case 'LEFT':
case 'RIGHT':
case 'STRAIGHT_JOIN':
$parseInfo['next_join_type'] = $upper;
break;
case ',':
$parseInfo['next_join_type'] = 'CROSS';
case 'JOIN':
if ($parseInfo['subquery']) {
$parseInfo['sub_tree'] = $this->parse($this->removeParenthesisFromStart($parseInfo['subquery']));
$parseInfo['expression'] = $parseInfo['subquery'];
}
$expr[] = $this->processFromExpression($parseInfo);
$parseInfo = $this->initParseInfo($parseInfo);
break;
default:
if ($upper === "") {
continue; // ends the switch statement!
}
if ($parseInfo['token_count'] === 0) {
if ($parseInfo['table'] === "") {
$parseInfo['table'] = $token;
$parseInfo['no_quotes'] = $this->revokeQuotation($token);
}
} elseif ($parseInfo['token_count'] === 1) {
$parseInfo['alias'] = array('as' => false, 'name' => trim($token),
'no_quotes' => $this->revokeQuotation($token),
'base_expr' => trim($token),
);
}
$parseInfo['token_count']++;
break;
}
++$i;
}
$expr[] = $this->processFromExpression($parseInfo);
return $expr;
}
}

View File

@@ -0,0 +1,73 @@
<?php
/**
* GroupByProcessor.php
*
* This file implements the processor for the GROUP-BY statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/OrderByProcessor.php';
/**
*
* This class processes the GROUP-BY statements.
*
* @author arothe
*
*/
class GroupByProcessor extends OrderByProcessor {
public function process($tokens, $select = array()) {
$out = array();
$parseInfo = $this->initParseInfo();
if (!$tokens) {
return false;
}
foreach ($tokens as $token) {
$trim = strtoupper(trim($token));
switch ($trim) {
case ',':
$parsed = $this->processOrderExpression($parseInfo, $select);
unset($parsed['direction']);
$out[] = $parsed;
$parseInfo = $this->initParseInfo();
break;
default:
$parseInfo['base_expr'] .= $token;
}
}
$parsed = $this->processOrderExpression($parseInfo, $select);
unset($parsed['direction']);
$out[] = $parsed;
return $out;
}
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* HavingProcessor.php
*
* This file implements the processor for the HAVING statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/ExpressionListProcessor.php';
/**
*
* This class processes the HAVING statements.
*
* @author arothe
*
*/
class HavingProcessor extends ExpressionListProcessor {
}

View File

@@ -0,0 +1,104 @@
<?php
/**
* IndexColumnListProcessor.php
*
* This file implements the processor for index column lists.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/../utils/ExpressionType.php';
/**
*
* This class processes the index column lists.
*
* @author arothe
*
*/
class IndexColumnListProcessor extends AbstractProcessor {
protected function initExpression() {
return array('name' => false, 'no_quotes' => false, 'length' => false, 'dir' => false);
}
public function process($sql) {
$tokens = $this->splitSQLIntoTokens($sql);
$expr = $this->initExpression();
$result = array();
$base_expr = "";
foreach ($tokens as $k => $token) {
$trim = trim($token);
$base_expr .= $token;
if ($trim === "") {
continue;
}
$upper = strtoupper($trim);
switch ($upper) {
case 'ASC':
case 'DESC':
// the optional order
$expr['dir'] = $trim;
break;
case ',':
// the next column
$result[] = array_merge(
array('expr_type' => ExpressionType::INDEX_COLUMN, 'base_expr' => $base_expr),
$expr
);
$expr = $this->initExpression();
$base_expr = "";
break;
default:
if ($upper[0] === '(' && substr($upper, -1) === ')') {
// the optional length
$expr['length'] = $this->removeParenthesisFromStart($trim);
continue 2;
}
// the col name
$expr['name'] = $trim;
$expr['no_quotes'] = $this->revokeQuotation($trim);
break;
}
}
$result[] = array_merge(array('expr_type' => ExpressionType::INDEX_COLUMN, 'base_expr' => $base_expr), $expr);
return $result;
}
}

View File

@@ -0,0 +1,74 @@
<?php
/**
* InsertProcessor.php
*
* This file implements the processor for the INSERT statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/ColumnListProcessor.php';
require_once dirname(__FILE__) . '/../utils/ExpressionType.php';
/**
*
* This class processes the INSERT statements.
*
* @author arothe
*
*/
class InsertProcessor extends AbstractProcessor {
public function process($tokenList, $token_category = 'INSERT') {
$table = "";
$cols = array();
$into = $tokenList['INTO'];
foreach ($into as $token) {
if ($this->isWhitespaceToken($token))
continue;
if ($table === "") {
$table = $token;
} elseif (empty($cols)) {
$cols[] = $token;
}
}
if (empty($cols)) {
$cols = false;
} else {
$processor = new ColumnListProcessor();
$cols = $processor->process($this->removeParenthesisFromStart($cols[0]));
}
unset($tokenList['INTO']);
$tokenList[$token_category][0] = array('table' => $table, 'columns' => $cols, 'base_expr' => $table,
'no_quotes' => $this->revokeQuotation($table),
);
return $tokenList;
}
}

View File

@@ -0,0 +1,58 @@
<?php
/**
* IntoProcessor.php
*
* This file implements the processor for the INTO statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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';
/**
*
* This class processes the INTO statements.
*
* @author arothe
*
*/
class IntoProcessor extends AbstractProcessor {
/**
* TODO: This is a dummy function, we cannot parse INTO as part of SELECT
* at the moment
*/
public function process($tokenList) {
$unparsed = $tokenList['INTO'];
foreach ($unparsed as $k => $token) {
if ($this->isWhitespaceToken($token) || $this->isCommaToken($token)) {
unset($unparsed[$k]);
}
}
$tokenList['INTO'] = array_values($unparsed);
return $tokenList;
}
}

View File

@@ -0,0 +1,83 @@
<?php
/**
* LimitProcessor.php
*
* This file implements the processor for the LIMIT statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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';
/**
*
* This class processes the LIMIT statements.
*
* @author arothe
*
*/
class LimitProcessor extends AbstractProcessor {
public function process($tokens) {
$rowcount = "";
$offset = "";
$comma = -1;
$exchange = false;
for ($i = 0; $i < count($tokens); ++$i) {
$trim = trim($tokens[$i]);
if ($trim === ",") {
$comma = $i;
break;
}
if ($trim === "OFFSET") {
$comma = $i;
$exchange = true;
break;
}
}
for ($i = 0; $i < $comma; ++$i) {
if ($exchange) {
$rowcount .= $tokens[$i];
} else {
$offset .= $tokens[$i];
}
}
for ($i = $comma + 1; $i < count($tokens); ++$i) {
if ($exchange) {
$offset .= $tokens[$i];
} else {
$rowcount .= $tokens[$i];
}
}
return array('offset' => trim($offset), 'rowcount' => trim($rowcount));
}
}

View File

@@ -0,0 +1,135 @@
<?php
/**
* OrderByProcessor.php
*
* This file implements the processor for the ORDER-BY statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/SelectExpressionProcessor.php';
require_once dirname(__FILE__) . '/../utils/ExpressionType.php';
/**
*
* This class processes the ORDER-BY statements.
*
* @author arothe
*
*/
class OrderByProcessor extends AbstractProcessor {
private $selectExpressionProcessor;
public function __construct() {
$this->selectExpressionProcessor = new SelectExpressionProcessor();
}
protected function initParseInfo() {
return array('base_expr' => "", 'dir' => "ASC", 'expr_type' => ExpressionType::EXPRESSION);
}
protected function processOrderExpression(&$parseInfo, $select) {
$parseInfo['base_expr'] = trim($parseInfo['base_expr']);
if ($parseInfo['base_expr'] === "") {
return false;
}
if (is_numeric($parseInfo['base_expr'])) {
$parseInfo['expr_type'] = ExpressionType::POSITION;
} else {
$parseInfo['no_quotes'] = $this->revokeQuotation($parseInfo['base_expr']);
// search to see if the expression matches an alias
foreach ($select as $clause) {
if (!$clause['alias']) {
continue;
}
if ($clause['alias']['no_quotes'] === $parseInfo['no_quotes']) {
$parseInfo['expr_type'] = ExpressionType::ALIAS;
break;
}
}
}
if ($parseInfo['expr_type'] === ExpressionType::EXPRESSION) {
$expr = $this->selectExpressionProcessor->process($parseInfo['base_expr']);
$expr['direction'] = $parseInfo['dir'];
unset($expr['alias']);
return $expr;
}
$result = array();
$result['expr_type'] = $parseInfo['expr_type'];
$result['base_expr'] = $parseInfo['base_expr'];
if (isset($parseInfo['no_quotes'])) {
$result['no_quotes'] = $parseInfo['no_quotes'];
}
$result['direction'] = $parseInfo['dir'];
return $result;
}
public function process($tokens, $select = array()) {
$out = array();
$parseInfo = $this->initParseInfo();
if (!$tokens) {
return false;
}
foreach ($tokens as $token) {
$upper = strtoupper(trim($token));
switch ($upper) {
case ',':
$out[] = $this->processOrderExpression($parseInfo, $select);
$parseInfo = $this->initParseInfo();
break;
case 'DESC':
$parseInfo['dir'] = "DESC";
break;
case 'ASC':
$parseInfo['dir'] = "ASC";
break;
default:
$parseInfo['base_expr'] .= $token;
}
}
$out[] = $this->processOrderExpression($parseInfo, $select);
return $out;
}
}

View File

@@ -0,0 +1,62 @@
<?php
/**
* RecordProcessor.php
*
* This file implements the processor for records.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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';
/**
*
* This class processes records.
*
* @author arothe
*
*/
class RecordProcessor extends AbstractProcessor {
private $expressionListProcessor;
public function __construct() {
$this->expressionListProcessor = new ExpressionListProcessor();
}
public function process($unparsed) {
$unparsed = $this->removeParenthesisFromStart($unparsed);
$values = $this->splitSQLIntoTokens($unparsed);
foreach ($values as $k => $v) {
if ($this->isCommaToken($v)) {
$values[$k] = "";
}
}
return $this->expressionListProcessor->process($values);
}
}

View File

@@ -0,0 +1,198 @@
<?php
/**
* ReferenceDefinitionProcessor.php
*
* This file implements the processor reference definition part of the CREATE TABLE statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/IndexColumnListProcessor.php';
require_once dirname(__FILE__) . '/../utils/ExpressionType.php';
/**
*
* This class processes the reference definition part of the CREATE TABLE statements.
*
* @author arothe
*/
class ReferenceDefinitionProcessor extends AbstractProcessor {
protected function buildReferenceDef($expr, $base_expr, $key) {
$expr['till'] = $key;
$expr['base_expr'] = $base_expr;
return $expr;
}
public function process($tokens) {
$expr = array('expr_type' => ExpressionType::REFERENCE, 'base_expr' => false, 'sub_tree' => array());
$base_expr = '';
foreach ($tokens as $key => $token) {
$trim = trim($token);
$base_expr .= $token;
if ($trim === '') {
continue;
}
$upper = strtoupper($trim);
switch ($upper) {
case ',':
// we stop on a single comma
// or at the end of the array $tokens
$expr = $this->buildReferenceDef($expr, trim(substr($base_expr, 0, -strlen($token))), $key - 1);
break 2;
case 'REFERENCES':
$expr['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$currCategory = $upper;
break;
case 'MATCH':
if ($currCategory === 'REF_COL_LIST') {
$expr['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$currCategory = 'REF_MATCH';
continue 2;
}
// else?
break;
case 'FULL':
case 'PARTIAL':
case 'SIMPLE':
if ($currCategory === 'REF_MATCH') {
$expr['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$expr['match'] = $upper;
$currCategory = 'REF_COL_LIST';
continue 2;
}
// else?
break;
case 'ON':
if ($currCategory === 'REF_COL_LIST') {
$expr['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$currCategory = 'REF_ACTION';
continue 2;
}
// else ?
break;
case 'UPDATE':
case 'DELETE':
if ($currCategory === 'REF_ACTION') {
$expr['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$currCategory = 'REF_OPTION_' . $upper;
continue 2;
}
// else ?
break;
case 'RESTRICT':
case 'CASCADE':
if (strpos($currCategory, 'REF_OPTION_') === 0) {
$expr['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$expr['on_' . strtolower(substr($currCategory, -6))] = $upper;
continue 2;
}
// else ?
break;
case 'SET':
case 'NO':
if (strpos($currCategory, 'REF_OPTION_') === 0) {
$expr['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$expr['on_' . strtolower(substr($currCategory, -6))] = $upper;
$currCategory = 'SEC_' . $currCategory;
continue 2;
}
// else ?
break;
case 'NULL':
case 'ACTION':
if (strpos($currCategory, 'SEC_REF_OPTION_') === 0) {
$expr['sub_tree'][] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $trim);
$expr['on_' . strtolower(substr($currCategory, -6))] .= ' ' . $upper;
$currCategory = 'REF_COL_LIST';
continue 2;
}
// else ?
break;
default:
switch ($currCategory) {
case 'REFERENCES':
if ($upper[0] === '(' && substr($upper, -1) === ')') {
// index_col_name list
$processor = new IndexColumnListProcessor();
$cols = $processor->process($this->removeParenthesisFromStart($trim));
$expr['sub_tree'][] = array('expr_type' => ExpressionType::COLUMN_LIST, 'base_expr' => $trim,
'sub_tree' => $cols,
);
$currCategory = 'REF_COL_LIST';
continue 3;
}
// foreign key reference table name
$expr['sub_tree'][] = array('expr_type' => ExpressionType::TABLE, 'table' => $trim,
'base_expr' => $trim, 'no_quotes' => $this->revokeQuotation($trim),
);
continue 3;
default:
// else ?
break;
}
break;
}
}
if (!isset($expr['till'])) {
$expr = $this->buildReferenceDef($expr, trim($base_expr), -1);
}
return $expr;
}
}

View File

@@ -0,0 +1,97 @@
<?php
/**
* RenameProcessor.php
*
* This file implements the processor for the RENAME statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/../utils/ExpressionToken.php';
require_once dirname(__FILE__) . '/../utils/ExpressionType.php';
/**
*
* This class processes the RENAME statements.
*
* @author arothe
*
*/
class RenameProcessor extends AbstractProcessor {
public function process($tokenList) {
$base_expr = "";
$resultList = array();
$tablePair = array();
foreach ($tokenList as $k => $v) {
$token = new ExpressionToken($k, $v);
if ($token->isWhitespaceToken()) {
continue;
}
switch ($token->getUpper()) {
case 'TO':
// separate source table from destination
$tablePair['source'] = array('expr_type' => ExpressionType::TABLE, 'table' => trim($base_expr),
'no_quotes' => $this->revokeQuotation($base_expr),
'base_expr' => $base_expr,
);
$base_expr = "";
break;
case ',':
// split rename operations
$tablePair['destination'] = array('expr_type' => ExpressionType::TABLE, 'table' => trim($base_expr),
'no_quotes' => $this->revokeQuotation($base_expr),
'base_expr' => $base_expr,
);
$resultList[] = $tablePair;
$tablePair = array();
$base_expr = "";
break;
default:
$base_expr .= $token->getToken();
break;
}
}
if ($base_expr !== "") {
$tablePair['destination'] = array('expr_type' => ExpressionType::TABLE, 'table' => trim($base_expr),
'no_quotes' => $this->revokeQuotation($base_expr),
'base_expr' => $base_expr,
);
$resultList[] = $tablePair;
}
return $resultList;
}
}

View File

@@ -0,0 +1,46 @@
<?php
/**
* ReplaceProcessor.php
*
* This file implements the processor for the REPLACE statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/InsertProcessor.php';
/**
*
* This class processes the REPLACE statements.
*
* @author arothe
*
*/
class ReplaceProcessor extends InsertProcessor {
public function process($tokenList) {
return parent::process($tokenList, 'REPLACE');
}
}

View File

@@ -0,0 +1,185 @@
<?php
/**
* SQLChunkProcessor.php
*
* This file implements the processor for the SQL chunks.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/FromProcessor.php';
require_once dirname(__FILE__) . '/RecordProcessor.php';
require_once dirname(__FILE__) . '/UpdateProcessor.php';
require_once dirname(__FILE__) . '/DeleteProcessor.php';
require_once dirname(__FILE__) . '/GroupByProcessor.php';
require_once dirname(__FILE__) . '/RenameProcessor.php';
require_once dirname(__FILE__) . '/UsingProcessor.php';
require_once dirname(__FILE__) . '/DescribeProcessor.php';
require_once dirname(__FILE__) . '/DescProcessor.php';
require_once dirname(__FILE__) . '/HavingProcessor.php';
require_once dirname(__FILE__) . '/ReplaceProcessor.php';
require_once dirname(__FILE__) . '/ValuesProcessor.php';
require_once dirname(__FILE__) . '/DropProcessor.php';
require_once dirname(__FILE__) . '/InsertProcessor.php';
require_once dirname(__FILE__) . '/SelectExpressionProcessor.php';
require_once dirname(__FILE__) . '/WhereProcessor.php';
require_once dirname(__FILE__) . '/DuplicateProcessor.php';
require_once dirname(__FILE__) . '/IntoProcessor.php';
require_once dirname(__FILE__) . '/SelectProcessor.php';
require_once dirname(__FILE__) . '/ExplainProcessor.php';
require_once dirname(__FILE__) . '/LimitProcessor.php';
require_once dirname(__FILE__) . '/SetProcessor.php';
require_once dirname(__FILE__) . '/ExpressionListProcessor.php';
require_once dirname(__FILE__) . '/OrderByProcessor.php';
require_once dirname(__FILE__) . '/ShowProcessor.php';
require_once dirname(__FILE__) . '/CreateProcessor.php';
require_once dirname(__FILE__) . '/TableProcessor.php';
/**
*
* This class processes the SQL chunks.
*
* @author arothe
*
*/
class SQLChunkProcessor extends AbstractProcessor {
protected function moveLIKE(&$out) {
if (!isset($out['TABLE']['like'])) {
return;
}
$out = $this->array_insert_after($out, 'TABLE', array('LIKE' => $out['TABLE']['like']));
unset($out['TABLE']['like']);
}
public function process($out) {
if (!$out) {
return false;
}
if (!empty($out['CREATE'])) {
$processor = new CreateProcessor();
$out['CREATE'] = $processor->process($out['CREATE']);
}
if (!empty($out['TABLE'])) {
$processor = new TableProcessor();
$out['TABLE'] = $processor->process($out['TABLE']);
$this->moveLIKE($out);
}
if (!empty($out['EXPLAIN'])) {
$processor = new ExplainProcessor();
$out['EXPLAIN'] = $processor->process($out['EXPLAIN'], array_keys($out));
}
if (!empty($out['DESCRIBE'])) {
$processor = new DescribeProcessor();
$out['DESCRIBE'] = $processor->process($out['DESCRIBE'], array_keys($out));
}
if (!empty($out['DESC'])) {
$processor = new DescProcessor();
$out['DESC'] = $processor->process($out['DESC'], array_keys($out));
}
if (!empty($out['SELECT'])) {
$processor = new SelectProcessor();
$out['SELECT'] = $processor->process($out['SELECT']);
}
if (!empty($out['FROM'])) {
$processor = new FromProcessor();
$out['FROM'] = $processor->process($out['FROM']);
}
if (!empty($out['USING'])) {
$processor = new UsingProcessor();
$out['USING'] = $processor->process($out['USING']);
}
if (!empty($out['UPDATE'])) {
$processor = new UpdateProcessor();
$out['UPDATE'] = $processor->process($out['UPDATE']);
}
if (!empty($out['GROUP'])) {
// set empty array if we have partial SQL statement
$processor = new GroupByProcessor();
$out['GROUP'] = $processor->process($out['GROUP'], isset($out['SELECT']) ? $out['SELECT'] : array());
}
if (!empty($out['ORDER'])) {
// set empty array if we have partial SQL statement
$processor = new OrderByProcessor();
$out['ORDER'] = $processor->process($out['ORDER'], isset($out['SELECT']) ? $out['SELECT'] : array());
}
if (!empty($out['LIMIT'])) {
$processor = new LimitProcessor();
$out['LIMIT'] = $processor->process($out['LIMIT']);
}
if (!empty($out['WHERE'])) {
$processor = new WhereProcessor();
$out['WHERE'] = $processor->process($out['WHERE']);
}
if (!empty($out['HAVING'])) {
$processor = new HavingProcessor();
$out['HAVING'] = $processor->process($out['HAVING']);
}
if (!empty($out['SET'])) {
$processor = new SetProcessor();
$out['SET'] = $processor->process($out['SET'], isset($out['UPDATE']));
}
if (!empty($out['DUPLICATE'])) {
$processor = new DuplicateProcessor();
$out['ON DUPLICATE KEY UPDATE'] = $processor->process($out['DUPLICATE']);
unset($out['DUPLICATE']);
}
if (!empty($out['INSERT'])) {
$processor = new InsertProcessor();
$out = $processor->process($out);
}
if (!empty($out['REPLACE'])) {
$processor = new ReplaceProcessor();
$out = $processor->process($out);
}
if (!empty($out['DELETE'])) {
$processor = new DeleteProcessor();
$out = $processor->process($out);
}
if (!empty($out['VALUES'])) {
$processor = new ValuesProcessor();
$out = $processor->process($out);
}
if (!empty($out['INTO'])) {
$processor = new IntoProcessor();
$out = $processor->process($out);
}
if (!empty($out['DROP'])) {
$processor = new DropProcessor();
$out['DROP'] = $processor->process($out['DROP']);
}
if (!empty($out['RENAME'])) {
$processor = new RenameProcessor();
$out['RENAME'] = $processor->process($out['RENAME']);
}
if (!empty($out['SHOW'])) {
$processor = new ShowProcessor();
$out['SHOW'] = $processor->process($out['SHOW']);
}
return $out;
}
}

View File

@@ -0,0 +1,498 @@
<?php
/**
* SQLProcessor.php
*
* This file implements the processor for the base SQL statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/SQLChunkProcessor.php';
/**
*
* This class processes the base SQL statements.
*
* @author arothe
*
*/
class SQLProcessor extends SQLChunkProcessor {
/*
* This function breaks up the SQL statement into logical sections.
* Some sections are then further handled by specialized processors.
*/
public function process($tokens) {
$prev_category = "";
$token_category = "";
$skip_next = 0;
$out = false;
$tokenCount = count($tokens);
for ($tokenNumber = 0; $tokenNumber < $tokenCount; ++$tokenNumber) {
$token = $tokens[$tokenNumber];
$trim = trim($token); // this removes also \n and \t!
// if it starts with an "(", it should follow a SELECT
if ($trim !== "" && $trim[0] === "(" && $token_category === "") {
$token_category = 'SELECT';
}
/*
* If it isn't obvious, when $skip_next is set, then we ignore the next real token, that is we ignore whitespace.
*/
if ($skip_next > 0) {
if ($trim === "") {
if ($token_category !== "") { // is this correct??
$out[$token_category][] = $token;
}
continue;
}
//to skip the token we replace it with whitespace
$trim = "";
$token = "";
$skip_next--;
if ($skip_next > 0) {
continue;
}
}
$upper = strtoupper($trim);
switch ($upper) {
/* Tokens that get their own sections. These keywords have subclauses. */
case 'SELECT':
case 'ORDER':
case 'DUPLICATE':
case 'VALUES':
case 'GROUP':
case 'HAVING':
case 'WHERE':
case 'CALL':
case 'PROCEDURE':
case 'FUNCTION':
case 'SERVER':
case 'LOGFILE':
case 'DEFINER':
case 'RETURNS':
case 'TABLESPACE':
case 'TRIGGER':
case 'DO':
case 'FLUSH':
case 'KILL':
case 'RESET':
case 'STOP':
case 'PURGE':
case 'EXECUTE':
case 'PREPARE':
case 'DEALLOCATE':
if ($trim === 'DEALLOCATE') {
$skip_next = 1;
}
$token_category = $upper;
break;
case 'SET':
if ($token_category !== 'TABLE') {
$token_category = $upper;
}
break;
case 'LIMIT':
case 'PLUGIN':
// no separate section
if ($token_category === 'SHOW') {
continue;
}
$token_category = $upper;
break;
case 'FROM':
// this FROM is different from FROM in other DML (not join related)
if ($token_category === 'PREPARE') {
continue 2;
}
// no separate section
if ($token_category === 'SHOW') {
continue;
}
$token_category = $upper;
break;
case 'EXPLAIN':
case 'DESCRIBE':
case 'SHOW':
$token_category = $upper;
break;
case 'DESC':
if ($token_category === '') {
// short version of DESCRIBE
$token_category = $upper;
}
// else direction of ORDER-BY
break;
case 'RENAME':
// jump over TABLE keyword
$token_category = $upper;
$skip_next = 1;
continue 2;
case 'DATABASE':
case 'SCHEMA':
if ($prev_category === 'DROP') {
continue;
}
if ($prev_category === 'SHOW') {
continue;
}
$token_category = $upper;
break;
case 'EVENT':
// issue 71
if ($prev_category === 'DROP' || $prev_category === 'ALTER' || $prev_category === 'CREATE') {
$token_category = $upper;
}
break;
case 'DATA':
// prevent wrong handling of DATA as keyword
if ($prev_category === 'LOAD') {
$token_category = $upper;
}
break;
case 'INTO':
// prevent wrong handling of CACHE within LOAD INDEX INTO CACHE...
if ($prev_category === 'LOAD') {
$out[$prev_category][] = $upper;
continue 2;
}
$token_category = $upper;
break;
case 'USER':
// prevent wrong processing as keyword
if ($prev_category === 'CREATE' || $prev_category === 'RENAME' || $prev_category === 'DROP') {
$token_category = $upper;
}
break;
case 'VIEW':
// prevent wrong processing as keyword
if ($prev_category === 'CREATE' || $prev_category === 'ALTER' || $prev_category === 'DROP') {
$token_category = $upper;
}
break;
/*
* These tokens get their own section, but have no subclauses. These tokens identify the statement but have no specific subclauses of their own.
*/
case 'DELETE':
case 'ALTER':
case 'INSERT':
case 'TRUNCATE':
case 'OPTIMIZE':
case 'GRANT':
case 'REVOKE':
case 'HANDLER':
case 'LOAD':
case 'ROLLBACK':
case 'SAVEPOINT':
case 'UNLOCK':
case 'INSTALL':
case 'UNINSTALL':
case 'ANALZYE':
case 'BACKUP':
case 'CHECKSUM':
case 'REPAIR':
case 'RESTORE':
case 'USE':
case 'HELP':
$token_category = $upper;
// set the category in case these get subclauses in a future version of MySQL
$out[$upper][0] = $upper;
continue 2;
case 'REPLACE':
if ($prev_category === 'TABLE') {
// part of the CREATE TABLE statement
$out[$prev_category][] = $upper;
continue 2;
}
// set the category in case these get subclauses in a future version of MySQL
$token_category = $upper;
$out[$upper][0] = $upper;
continue 2;
case 'IGNORE':
if ($prev_category === 'TABLE') {
// part of the CREATE TABLE statement
$out[$prev_category][] = $upper;
continue 2;
}
$out['OPTIONS'][] = $upper;
continue 2;
break;
case 'CHECK':
if ($prev_category === 'TABLE') {
$out[$prev_category][] = $upper;
continue 2;
}
$token_category = $upper;
$out[$upper][0] = $upper;
continue 2;
case 'CREATE':
if ($prev_category === 'SHOW') {
continue;
}
$token_category = $upper;
break;
case 'TABLE':
if ($prev_category === 'CREATE') {
$out[$prev_category][] = $upper;
$token_category = $upper;
}
break;
case 'TEMPORARY':
if ($prev_category === 'CREATE') {
$out[$prev_category][] = $upper;
$token_category = $prev_category;
continue 2;
}
break;
case 'IF':
if ($prev_category === 'TABLE') {
$token_category = 'CREATE';
$out[$token_category] = array_merge($out[$token_category], $out[$prev_category]);
$out[$prev_category] = array();
$out[$token_category][] = $upper;
$prev_category = $token_category;
continue 2;
}
break;
case 'NOT':
if ($prev_category === 'CREATE') {
$token_category = $prev_category;
$out[$prev_category][] = $upper;
continue 2;
}
break;
case 'EXISTS':
if ($prev_category === 'CREATE') {
$out[$prev_category][] = $upper;
$prev_category = $token_category = 'TABLE';
continue 2;
}
break;
case 'CACHE':
if ($prev_category === "" || $prev_category === 'RESET' || $prev_category === 'FLUSH'
|| $prev_category === 'LOAD') {
$token_category = $upper;
continue 2;
}
break;
/* This is either LOCK TABLES or SELECT ... LOCK IN SHARE MODE */
case 'LOCK':
if ($token_category === "") {
$token_category = $upper;
$out[$upper][0] = $upper;
} else {
$trim = 'LOCK IN SHARE MODE';
$skip_next = 3;
$out['OPTIONS'][] = $trim;
}
continue 2;
break;
case 'USING': /* USING in FROM clause is different from USING w/ prepared statement*/
if ($token_category === 'EXECUTE') {
$token_category = $upper;
continue 2;
}
if ($token_category === 'FROM' && !empty($out['DELETE'])) {
$token_category = $upper;
continue 2;
}
break;
/* DROP TABLE is different from ALTER TABLE DROP ... */
case 'DROP':
if ($token_category !== 'ALTER') {
$token_category = $upper;
continue 2;
}
break;
case 'FOR':
if ($prev_category === 'SHOW') {
continue;
}
$skip_next = 1;
$out['OPTIONS'][] = 'FOR UPDATE';
continue 2;
break;
case 'UPDATE':
if ($token_category === "") {
$token_category = $upper;
continue 2;
}
if ($token_category === 'DUPLICATE') {
continue 2;
}
break;
case 'START':
$trim = "BEGIN";
$out[$upper][0] = $upper;
$skip_next = 1;
break;
/* These tokens are ignored. */
case 'TO':
if ($token_category === 'RENAME') {
break;
}
case 'BY':
case 'ALL':
case 'SHARE':
case 'MODE':
case ';':
continue 2;
break;
case 'KEY':
if ($token_category === 'DUPLICATE') {
continue 2;
}
break;
/* These tokens set particular options for the statement. They never stand alone. */
case 'LOW_PRIORITY':
case 'DELAYED':
case 'FORCE':
case 'QUICK':
$out['OPTIONS'][] = $upper;
continue 2;
break;
case 'WITH':
if ($token_category === 'GROUP') {
$skip_next = 1;
$out['OPTIONS'][] = 'WITH ROLLUP';
continue 2;
}
break;
case 'AS':
break;
case '':
case ',':
case ';':
break;
default:
break;
}
// remove obsolete category after union (empty category because of
// empty token before select)
if ($token_category !== "" && ($prev_category === $token_category)) {
$out[$token_category][] = $token;
}
$prev_category = $token_category;
}
return parent::process($out);
}
}

View File

@@ -0,0 +1,171 @@
<?php
/**
* SelectExpressionProcessor.php
*
* This file implements the processor for SELECT expressions.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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 = implode("", $tokens);
}
}
if (!$alias) {
$base_expr = implode("", $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;
}
}

View File

@@ -0,0 +1,86 @@
<?php
/**
* SelectProcessor.php
*
* This file implements the processor for the SELECT statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/SelectExpressionProcessor.php';
/**
*
* This class processes the SELECT statements.
*
* @author arothe
*
*/
class SelectProcessor extends SelectExpressionProcessor {
public function process($tokens) {
$expression = "";
$expressionList = array();
foreach ($tokens as $token) {
if ($this->isCommaToken($token)) {
$expression = parent::process(trim($expression));
$expression['delim'] = ',';
$expressionList[] = $expression;
$expression = "";
} else {
switch (strtoupper($token)) {
// add more SELECT options here
case 'DISTINCT':
case 'DISTINCTROW':
case 'HIGH_PRIORITY':
case 'SQL_CACHE':
case 'SQL_NO_CACHE':
case 'SQL_CALC_FOUND_ROWS':
case 'STRAIGHT_JOIN':
case 'SQL_SMALL_RESULT':
case 'SQL_BIG_RESULT':
case 'SQL_BUFFER_RESULT':
$expression = parent::process(trim($token));
$expression['delim'] = ' ';
$expressionList[] = $expression;
$expression = "";
break;
default:
$expression .= $token;
}
}
}
if ($expression) {
$expression = parent::process(trim($expression));
$expression['delim'] = false;
$expressionList[] = $expression;
}
return $expressionList;
}
}

View File

@@ -0,0 +1,111 @@
<?php
/**
* SetProcessor.php
*
* This file implements the processor for the SET statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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 SET statements.
*
* @author arothe
*
*/
class SetProcessor extends AbstractProcessor {
private $expressionListProcessor;
public function __construct() {
$this->expressionListProcessor = new ExpressionListProcessor();
}
/**
* A SET list is simply a list of key = value expressions separated by comma (,).
* This function produces a list of the key/value expressions.
*/
protected function getAssignment($base_expr) {
$assignment = $this->expressionListProcessor->process($this->splitSQLIntoTokens($base_expr));
return array('expr_type' => ExpressionType::EXPRESSION, 'base_expr' => trim($base_expr),
'sub_tree' => $assignment,
);
}
public function process($tokens, $isUpdate = false) {
$result = array();
$baseExpr = "";
$assignment = false;
$varType = false;
foreach ($tokens as $token) {
$upper = strtoupper(trim($token));
switch ($upper) {
case 'LOCAL':
case 'SESSION':
case 'GLOBAL':
if (!$isUpdate) {
$varType = $this->getVariableType("@@" . $upper . ".");
$baseExpr = "";
continue 2;
}
break;
case ',':
$assignment = $this->getAssignment($baseExpr);
if (!$isUpdate && $varType !== false) {
$assignment['sub_tree'][0]['expr_type'] = $varType;
}
$result[] = $assignment;
$baseExpr = "";
$varType = false;
continue 2;
default:
}
$baseExpr .= $token;
}
if (trim($baseExpr) !== "") {
$assignment = $this->getAssignment($baseExpr);
if (!$isUpdate && $varType !== false) {
$assignment['sub_tree'][0]['expr_type'] = $varType;
}
$result[] = $assignment;
}
return $result;
}
}

View File

@@ -0,0 +1,182 @@
<?php
/**
* ShowProcessor.php
*
* This file implements the processor for the SHOW statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/../utils/PHPSQLParserConstants.php';
require_once dirname(__FILE__) . '/../utils/ExpressionType.php';
require_once dirname(__FILE__) . '/LimitProcessor.php';
require_once dirname(__FILE__) . '/AbstractProcessor.php';
/**
*
* This class processes the SHOW statements.
*
* @author arothe
*
*/
class ShowProcessor extends AbstractProcessor {
private $limitProcessor;
public function __construct() {
$this->limitProcessor = new LimitProcessor();
}
public function process($tokens) {
$resultList = array();
$category = "";
$prev = "";
foreach ($tokens as $k => $token) {
$upper = strtoupper(trim($token));
if ($this->isWhitespaceToken($token)) {
continue;
}
switch ($upper) {
case 'FROM':
$resultList[] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => trim($token));
if ($prev === 'INDEX' || $prev === 'COLUMNS') {
continue;
}
$category = $upper;
break;
case 'CREATE':
case 'DATABASE':
case 'FUNCTION':
case 'PROCEDURE':
case 'ENGINE':
case 'TABLE':
case 'FOR':
case 'LIKE':
case 'INDEX':
case 'COLUMNS':
case 'PLUGIN':
case 'PRIVILEGES':
case 'PROCESSLIST':
case 'LOGS':
case 'STATUS':
case 'GLOBAL':
case 'SESSION':
case 'FULL':
case 'GRANTS':
case 'INNODB':
case 'STORAGE':
case 'ENGINES':
case 'OPEN':
case 'BDB':
case 'TRIGGERS':
case 'VARIABLES':
case 'DATABASES':
case 'ERRORS':
case 'TABLES':
case 'WARNINGS':
case 'CHARACTER':
case 'SET':
case 'COLLATION':
$resultList[] = array('expr_type' => ExpressionType::RESERVED, 'base_expr' => trim($token));
$category = $upper;
break;
default:
switch ($prev) {
case 'LIKE':
$resultList[] = array('expr_type' => ExpressionType::CONSTANT, 'base_expr' => $token);
break;
case 'LIMIT':
$limit = array_pop($resultList);
$limit['sub_tree'] = $this->limitProcessor->process(array_slice($tokens, $k));
$resultList[] = $limit;
break;
case 'FROM':
case 'DATABASE':
$resultList[] = array('expr_type' => ExpressionType::DATABASE, 'name' => $token,
'no_quotes' => $this->revokeQuotation($token), 'base_expr' => $token,
);
break;
case 'FOR':
$resultList[] = array('expr_type' => ExpressionType::USER, 'name' => $token,
'no_quotes' => $this->revokeQuotation($token), 'base_expr' => $token,
);
break;
case 'INDEX':
case 'COLUMNS':
case 'TABLE':
$resultList[] = array('expr_type' => ExpressionType::TABLE, 'table' => $token,
'no_quotes' => $this->revokeQuotation($token), 'base_expr' => $token,
);
$category = "TABLENAME";
break;
case 'FUNCTION':
if (PHPSQLParserConstants::isAggregateFunction($upper)) {
$expr_type = ExpressionType::AGGREGATE_FUNCTION;
} else {
$expr_type = ExpressionType::SIMPLE_FUNCTION;
}
$resultList[] = array('expr_type' => $expr_type, 'name' => $token,
'no_quotes' => $this->revokeQuotation($token), 'base_expr' => $token,
);
break;
case 'PROCEDURE':
$resultList[] = array('expr_type' => ExpressionType::PROCEDURE, 'name' => $token,
'no_quotes' => $this->revokeQuotation($token), 'base_expr' => $token,
);
break;
case 'ENGINE':
$resultList[] = array('expr_type' => ExpressionType::ENGINE, 'name' => $token,
'no_quotes' => $this->revokeQuotation($token), 'base_expr' => $token,
);
break;
default:
// ignore
break;
}
break;
}
$prev = $category;
}
return $resultList;
}
}

View File

@@ -0,0 +1,402 @@
<?php
/**
* TableProcessor.php
*
* This file implements the processor for the TABLE statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/CreateDefinitionProcessor.php';
require_once dirname(__FILE__) . '/../utils/ExpressionType.php';
/**
*
* This class processes the TABLE statements.
*
* @author arothe
*
*/
class TableProcessor extends AbstractProcessor {
protected function getReservedType($token) {
return array('expr_type' => ExpressionType::RESERVED, 'base_expr' => $token);
}
protected function getConstantType($token) {
return array('expr_type' => ExpressionType::CONSTANT, 'base_expr' => $token);
}
protected function getOperatorType($token) {
return array('expr_type' => ExpressionType::OPERATOR, 'base_expr' => $token);
}
protected function clear(&$expr, &$base_expr, &$category) {
$expr = array();
$base_expr = '';
$category = 'CREATE_DEF';
}
public function process($tokens) {
$currCategory = "TABLE_NAME";
$result = array('base_expr' => false, 'name' => false, 'no_quotes' => false, 'create-def' => false,
'options' => false, 'like' => false, 'select-option' => false,
);
$expr = array();
$base_expr = '';
$skip = 0;
foreach ($tokens as $token) {
$trim = trim($token);
$base_expr .= $token;
if ($skip > 0) {
$skip--;
continue;
}
if ($skip < 0) {
break;
}
if ($trim === "") {
continue;
}
$upper = strtoupper($trim);
switch ($upper) {
case ',':
// it is possible to separate the table options with comma!
if ($prevCategory === 'CREATE_DEF') {
$last = array_pop($result['options']);
$last['delim'] = ',';
$result['options'][] = $last;
$base_expr = "";
}
continue 2;
case 'UNION':
if ($prevCategory === 'CREATE_DEF') {
$expr[] = $this->getReservedType($trim);
$currCategory = 'UNION';
continue 2;
}
break;
case 'LIKE':
// like without parenthesis
if ($prevCategory === 'TABLE_NAME') {
$currCategory = $upper;
continue 2;
}
break;
case '=':
// the optional operator
if ($prevCategory === 'TABLE_OPTION') {
$expr[] = $this->getOperatorType($trim);
continue 2; // don't change the category
}
break;
case 'CHARACTER':
if ($prevCategory === 'CREATE_DEF') {
$expr[] = $this->getReservedType($trim);
$currCategory = 'TABLE_OPTION';
}
if ($prevCategory === 'TABLE_OPTION') {
// add it to the previous DEFAULT
$expr[] = $this->getReservedType($trim);
continue 2;
}
break;
case 'SET':
if ($prevCategory === 'TABLE_OPTION') {
// add it to a previous CHARACTER
$expr[] = $this->getReservedType($trim);
$currCategory = 'CHARSET';
continue 2;
}
break;
case 'COLLATE':
if ($prevCategory === 'TABLE_OPTION') {
// add it to the previous DEFAULT
$expr[] = $this->getReservedType($trim);
$currCategory = 'COLLATE';
continue 2;
}
break;
case 'DIRECTORY':
if ($currCategory === 'INDEX_DIRECTORY' || $currCategory === 'DATA_DIRECTORY') {
// after INDEX or DATA
$expr[] = $this->getReservedType($trim);
continue 2;
}
break;
case 'INDEX':
if ($prevCategory === 'CREATE_DEF') {
$expr[] = $this->getReservedType($trim);
$currCategory = 'INDEX_DIRECTORY';
continue 2;
}
break;
case 'DATA':
if ($prevCategory === 'CREATE_DEF') {
$expr[] = $this->getReservedType($trim);
$currCategory = 'DATA_DIRECTORY';
continue 2;
}
break;
case 'INSERT_METHOD':
case 'DELAY_KEY_WRITE':
case 'ROW_FORMAT':
case 'PASSWORD':
case 'MAX_ROWS':
case 'MIN_ROWS':
case 'PACK_KEYS':
case 'CHECKSUM':
case 'COMMENT':
case 'CONNECTION':
case 'AUTO_INCREMENT':
case 'AVG_ROW_LENGTH':
case 'ENGINE':
case 'TYPE':
case 'STATS_AUTO_RECALC':
case 'STATS_PERSISTENT':
case 'KEY_BLOCK_SIZE':
if ($prevCategory === 'CREATE_DEF') {
$expr[] = $this->getReservedType($trim);
$currCategory = $prevCategory = 'TABLE_OPTION';
continue 2;
}
break;
case 'DYNAMIC':
case 'FIXED':
case 'COMPRESSED':
case 'REDUNDANT':
case 'COMPACT':
case 'NO':
case 'FIRST':
case 'LAST':
case 'DEFAULT':
if ($prevCategory === 'CREATE_DEF') {
// DEFAULT before CHARACTER SET and COLLATE
$expr[] = $this->getReservedType($trim);
$currCategory = 'TABLE_OPTION';
}
if ($prevCategory === 'TABLE_OPTION') {
// all assignments with the keywords
$expr[] = $this->getReservedType($trim);
$result['options'][] = array('expr_type' => ExpressionType::EXPRESSION,
'base_expr' => trim($base_expr), 'delim' => ' ',
'sub_tree' => $expr,
);
$this->clear($expr, $base_expr, $currCategory);
}
break;
case 'IGNORE':
case 'REPLACE':
$expr[] = $this->getReservedType($trim);
$result['select-option'] = array('base_expr' => trim($base_expr), 'duplicates' => $trim, 'as' => false,
'sub_tree' => $expr,
);
continue 2;
case 'AS':
$expr[] = $this->getReservedType($trim);
if (!isset($result['select-option']['duplicates'])) {
$result['select-option']['duplicates'] = false;
}
$result['select-option']['as'] = true;
$result['select-option']['base_expr'] = trim($base_expr);
$result['select-option']['sub_tree'] = $expr;
continue 2;
case 'PARTITION':
// TODO: parse partition options
$skip = -1;
break;
default:
switch ($currCategory) {
case 'CHARSET':
// the charset name
$expr[] = $this->getConstantType($trim);
$result['options'][] = array('expr_type' => ExpressionType::CHARSET,
'base_expr' => trim($base_expr), 'delim' => ' ',
'sub_tree' => $expr,
);
$this->clear($expr, $base_expr, $currCategory);
break;
case 'COLLATE':
// the collate name
$expr[] = $this->getConstantType($trim);
$result['options'][] = array('expr_type' => ExpressionType::COLLATE,
'base_expr' => trim($base_expr), 'delim' => ' ',
'sub_tree' => $expr,
);
$this->clear($expr, $base_expr, $currCategory);
break;
case 'DATA_DIRECTORY':
// we have the directory name
$expr[] = $this->getConstantType($trim);
$result['options'][] = array('expr_type' => ExpressionType::DIRECTORY, 'kind' => 'DATA',
'base_expr' => trim($base_expr), 'delim' => ' ',
'sub_tree' => $expr,
);
$this->clear($expr, $base_expr, $prevCategory);
continue 3;
case 'INDEX_DIRECTORY':
// we have the directory name
$expr[] = $this->getConstantType($trim);
$result['options'][] = array('expr_type' => ExpressionType::DIRECTORY, 'kind' => 'INDEX',
'base_expr' => trim($base_expr), 'delim' => ' ',
'sub_tree' => $expr,
);
$this->clear($expr, $base_expr, $prevCategory);
continue 3;
case 'TABLE_NAME':
$result['base_expr'] = $result['name'] = $trim;
$result['no_quotes'] = $this->revokeQuotation($trim);
$this->clear($expr, $base_expr, $prevCategory);
break;
case 'LIKE':
$result['like'] = array('expr_type' => ExpressionType::TABLE, 'table' => $trim, 'base_expr' => $trim,
'no_quotes' => $this->revokeQuotation($trim),
);
$this->clear($expr, $base_expr, $currCategory);
break;
case '':
// after table name
if ($prevCategory === 'TABLE_NAME' && $upper[0] === '(' && substr($upper, -1) === ')') {
$unparsed = $this->splitSQLIntoTokens($this->removeParenthesisFromStart($trim));
$processor = new CreateDefinitionProcessor();
$coldef = $processor->process($unparsed);
$result['create-def'] = array('expr_type' => ExpressionType::BRACKET_EXPRESSION,
'base_expr' => $base_expr, 'sub_tree' => $coldef['create-def'],
);
$expr = array();
$base_expr = '';
$currCategory = 'CREATE_DEF';
}
break;
case 'UNION':
// TODO: this token starts and ends with parenthesis
// and contains a list of table names (comma-separated)
// split the token and add the list as subtree
// we must change the DefaultProcessor
$unparsed = $this->splitSQLIntoTokens($this->removeParenthesisFromStart($trim));
$expr[] = array('expr_type' => ExpressionType::BRACKET_EXPRESSION, 'base_expr' => $trim,
'sub_tree' => '***TODO***',
);
$result['options'][] = array('expr_type' => ExpressionType::UNION, 'base_expr' => trim($base_expr),
'delim' => ' ', 'sub_tree' => $expr,
);
$this->clear($expr, $base_expr, $currCategory);
break;
default:
// strings and numeric constants
$expr[] = $this->getConstantType($trim);
$result['options'][] = array('expr_type' => ExpressionType::EXPRESSION,
'base_expr' => trim($base_expr), 'delim' => ' ',
'sub_tree' => $expr,
);
$this->clear($expr, $base_expr, $currCategory);
break;
}
break;
}
$prevCategory = $currCategory;
$currCategory = "";
}
if ($result['like'] === false) {
unset($result['like']);
}
if ($result['select-option'] === false) {
unset($result['select-option']);
}
return $result;
}
}

View File

@@ -0,0 +1,169 @@
<?php
/**
* UnionProcessor.php
*
* This file implements the processor for the UNION statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/SQLProcessor.php';
require_once dirname(__FILE__) . '/DefaultProcessor.php';
require_once dirname(__FILE__) . '/../utils/ExpressionType.php';
/**
*
* This class processes the UNION statements.
*
* @author arothe
*
*/
class UnionProcessor extends AbstractProcessor {
public function isUnion($queries) {
$unionTypes = array('UNION', 'UNION ALL');
foreach ($unionTypes as $unionType) {
if (!empty($queries[$unionType])) {
return true;
}
}
return false;
}
/**
* MySQL supports a special form of UNION:
* (select ...)
* union
* (select ...)
*
* This function handles this query syntax. Only one such subquery
* is supported in each UNION block. (select)(select)union(select) is not legal.
* The extra queries will be silently ignored.
*/
protected function processMySQLUnion($queries) {
$unionTypes = array('UNION', 'UNION ALL');
foreach ($unionTypes as $unionType) {
if (empty($queries[$unionType])) {
continue;
}
foreach ($queries[$unionType] as $key => $tokenList) {
foreach ($tokenList as $z => $token) {
$token = trim($token);
if ($token === "") {
continue;
}
// starts with "(select"
if (preg_match("/^\\(\\s*select\\s*/i", $token)) {
$processor = new DefaultProcessor();
$queries[$unionType][$key] = $processor->process($this->removeParenthesisFromStart($token));
break;
}
$processor = new SQLProcessor();
$queries[$unionType][$key] = $processor->process($queries[$unionType][$key]);
break;
}
}
}
// it can be parsed or not
return $queries;
}
public function process($inputArray) {
$outputArray = array();
// ometimes the parser needs to skip ahead until a particular
// oken is found
$skipUntilToken = false;
// his is the last type of union used (UNION or UNION ALL)
// ndicates a) presence of at least one union in this query
// b) the type of union if this is the first or last query
$unionType = false;
// ometimes a "query" consists of more than one query (like a UNION query)
// his array holds all the queries
$queries = array();
foreach ($inputArray as $key => $token) {
$trim = trim($token);
// overread all tokens till that given token
if ($skipUntilToken) {
if ($trim === "") {
continue; // read the next token
}
if (strtoupper($trim) === $skipUntilToken) {
$skipUntilToken = false;
continue; // read the next token
}
}
if (strtoupper($trim) !== "UNION") {
$outputArray[] = $token; // here we get empty tokens, if we remove these, we get problems in parse_sql()
continue;
}
$unionType = "UNION";
// we are looking for an ALL token right after UNION
for ($i = $key + 1; $i < count($inputArray); ++$i) {
if (trim($inputArray[$i]) === "") {
continue;
}
if (strtoupper($inputArray[$i]) !== "ALL") {
break;
}
// the other for-loop should overread till "ALL"
$skipUntilToken = "ALL";
$unionType = "UNION ALL";
}
// store the tokens related to the unionType
$queries[$unionType][] = $outputArray;
$outputArray = array();
}
// the query tokens after the last UNION or UNION ALL
// or we don't have an UNION/UNION ALL
if (!empty($outputArray)) {
if ($unionType) {
$queries[$unionType][] = $outputArray;
} else {
$queries[] = $outputArray;
}
}
return $this->processMySQLUnion($queries);
}
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* UpdateProcessor.php
*
* This file implements the processor for the UPDATE statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/FromProcessor.php';
/**
*
* This class processes the UPDATE statements.
*
* @author arothe
*
*/
class UpdateProcessor extends FromProcessor {
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* UsingProcessor.php
*
* This file implements the processor for the USING statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/FromProcessor.php';
/**
*
* This class processes the USING statements.
*
* @author arothe
*
*/
class UsingProcessor extends FromProcessor {
}

View File

@@ -0,0 +1,78 @@
<?php
/**
* ValuesProcessor.php
*
* This file implements the processor for the VALUES statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/../utils/ExpressionType.php';
require_once dirname(__FILE__) . '/RecordProcessor.php';
require_once dirname(__FILE__) . '/AbstractProcessor.php';
/**
*
* This class processes the VALUES statements.
*
* @author arothe
*
*/
class ValuesProcessor extends AbstractProcessor {
private $recordProcessor;
public function __construct() {
$this->recordProcessor = new RecordProcessor();
}
public function process($tokens) {
$unparsed = "";
foreach ($tokens['VALUES'] as $k => $v) {
if ($this->isWhitespaceToken($v)) {
continue;
}
$unparsed .= $v;
}
$values = $this->splitSQLIntoTokens($unparsed);
$parsed = array();
foreach ($values as $k => $v) {
if ($this->isCommaToken($v)) {
unset($values[$k]);
} else {
$processor = new RecordProcessor();
$values[$k] = array('expr_type' => ExpressionType::RECORD, 'base_expr' => $v,
'data' => $this->recordProcessor->process($v),
);
}
}
$tokens['VALUES'] = array_values($values);
return $tokens;
}
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* WhereProcessor.php
*
* This file implements the processor for the WHERE statements.
*
* Copyright (c) 2010-2012, Justin Swanhart
* with contributions by André Rothe <arothe@phosco.info, phosco@gmx.de>
*
* 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__) . '/ExpressionListProcessor.php';
/**
*
* This class processes the WHERE statements.
*
* @author arothe
*
*/
class WhereProcessor extends ExpressionListProcessor {
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;