Localization.
Updated sql-parser library to udan11/sql-parser@e1b91ed. Signed-off-by: Dan Ungureanu <udan1107@gmail.com>
This commit is contained in:
parent
86a4c3e32d
commit
2b3febf652
3
js/codemirror/addon/lint/sql-lint.js
vendored
3
js/codemirror/addon/lint/sql-lint.js
vendored
@ -30,7 +30,8 @@ CodeMirror.sqlLint = function(text, updateLinting, options, cm) {
|
||||
method: "POST",
|
||||
url: "lint.php",
|
||||
data: {
|
||||
'sql_query': text
|
||||
sql_query: text,
|
||||
token: PMA_commonParams.get('token'),
|
||||
},
|
||||
success: handleResponse
|
||||
});
|
||||
|
||||
@ -78,19 +78,16 @@ class PMA_Linter
|
||||
{
|
||||
// Disabling lint for huge queries to save some resources.
|
||||
if (/*overload*/mb_strlen($query) > 10000) {
|
||||
echo json_encode(
|
||||
return array(
|
||||
array(
|
||||
array(
|
||||
'message' => 'Linting is disabled for this query because it exceeds the maximum length.',
|
||||
'fromLine' => 0,
|
||||
'fromColumn' => 0,
|
||||
'toLine' => 0,
|
||||
'toColumn' => 0,
|
||||
'severity' => 'warning',
|
||||
)
|
||||
'message' => __('Linting is disabled for this query because it exceeds the maximum length.'),
|
||||
'fromLine' => 0,
|
||||
'fromColumn' => 0,
|
||||
'toLine' => 0,
|
||||
'toColumn' => 0,
|
||||
'severity' => 'warning',
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -146,7 +143,10 @@ class PMA_Linter
|
||||
|
||||
// Building the response.
|
||||
$response[] = array(
|
||||
'message' => $error[0] . ' (near <code>' . $error[2] . '</code>)',
|
||||
'message' => sprintf(
|
||||
__('%1$s (near <code>%2$s</code>)'),
|
||||
$error[0], $error[2]
|
||||
),
|
||||
'fromLine' => $fromLine,
|
||||
'fromColumn' => $fromColumn,
|
||||
'toLine' => $toLine,
|
||||
@ -156,7 +156,7 @@ class PMA_Linter
|
||||
}
|
||||
|
||||
// Sending back the answer.
|
||||
echo json_encode($response);
|
||||
return $response;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -58,7 +58,8 @@ $strConfigCodemirrorEnable_desc = __(
|
||||
);
|
||||
$strConfigCodemirrorEnable_name = __('Enable CodeMirror');
|
||||
$strConfigLintEnable_desc = __('Find any errors in the query before executing it.'
|
||||
. ' Requires CodeMirror to be enabled.');
|
||||
. ' Requires CodeMirror to be enabled.'
|
||||
);
|
||||
$strConfigLintEnable_name = __('Enable linter');
|
||||
$strConfigMinSizeForInputField_desc = __(
|
||||
'Defines the minimum size for input fields generated for CHAR and VARCHAR '
|
||||
|
||||
@ -35,9 +35,7 @@ abstract class Component
|
||||
* @return mixed
|
||||
*/
|
||||
public static function parse(
|
||||
Parser $parser,
|
||||
TokensList $list,
|
||||
array $options = array()
|
||||
Parser $parser, TokensList $list, array $options = array()
|
||||
) {
|
||||
// This method should be abstract, but it can't be both static and
|
||||
// abstract.
|
||||
|
||||
@ -187,7 +187,10 @@ class AlterOperation extends Component
|
||||
}
|
||||
|
||||
if ($ret->options->isEmpty()) {
|
||||
$parser->error('Unrecognized alter operation.', $list->tokens[$list->idx]);
|
||||
$parser->error(
|
||||
__('Unrecognized alter operation.'),
|
||||
$list->tokens[$list->idx]
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@ -85,7 +85,14 @@ class Array2d extends Component
|
||||
if ($count === -1) {
|
||||
$count = $arrCount;
|
||||
} elseif ($arrCount != $count) {
|
||||
$parser->error("{$count} values were expected, but found {$arrCount}.", $token);
|
||||
$parser->error(
|
||||
sprintf(
|
||||
__('%1$d values were expected, but found %2$d.'),
|
||||
$count,
|
||||
$arrCount
|
||||
),
|
||||
$token
|
||||
);
|
||||
}
|
||||
$ret[] = $arr;
|
||||
$state = 1;
|
||||
@ -103,7 +110,7 @@ class Array2d extends Component
|
||||
|
||||
if ($state === 0) {
|
||||
$parser->error(
|
||||
'An opening bracket followed by a set of values was expected.',
|
||||
__('An opening bracket followed by a set of values was expected.'),
|
||||
$list->tokens[$list->idx]
|
||||
);
|
||||
}
|
||||
|
||||
@ -97,7 +97,10 @@ class ArrayObj extends Component
|
||||
|
||||
if ($state === 0) {
|
||||
if (($token->type !== Token::TYPE_OPERATOR) || ($token->value !== '(')) {
|
||||
$parser->error('An opening bracket was expected.', $token);
|
||||
$parser->error(
|
||||
__('An opening bracket was expected.'),
|
||||
$token
|
||||
);
|
||||
break;
|
||||
}
|
||||
$state = 1;
|
||||
@ -111,7 +114,10 @@ class ArrayObj extends Component
|
||||
$state = 2;
|
||||
} elseif ($state === 2) {
|
||||
if (($token->type !== Token::TYPE_OPERATOR) || (($token->value !== ',') && ($token->value !== ')'))) {
|
||||
$parser->error('A comma or a closing bracket was expected', $token);
|
||||
$parser->error(
|
||||
__('A comma or a closing bracket was expected'),
|
||||
$token
|
||||
);
|
||||
break;
|
||||
}
|
||||
if ($token->value === ',') {
|
||||
|
||||
@ -122,7 +122,7 @@ class DataType extends Component
|
||||
if ($state === 0) {
|
||||
$ret->name = strtoupper($token->value);
|
||||
if (($token->type !== Token::TYPE_KEYWORD) || (!($token->flags & Token::FLAG_KEYWORD_DATA_TYPE))) {
|
||||
$parser->error('Unrecognized data type.', $token);
|
||||
$parser->error(__('Unrecognized data type.'), $token);
|
||||
}
|
||||
$state = 1;
|
||||
} elseif ($state === 1) {
|
||||
|
||||
@ -219,7 +219,7 @@ class Expression extends Component
|
||||
break;
|
||||
}
|
||||
} elseif ($brackets < 0) {
|
||||
$parser->error('Unexpected closing bracket.', $token);
|
||||
$parser->error(__('Unexpected closing bracket.'), $token);
|
||||
$brackets = 0;
|
||||
}
|
||||
} elseif ($token->value === ',') {
|
||||
@ -240,7 +240,7 @@ class Expression extends Component
|
||||
if ($alias) {
|
||||
// An alias is expected (the keyword `AS` was previously found).
|
||||
if (!empty($ret->alias)) {
|
||||
$parser->error('An alias was previously found.', $token);
|
||||
$parser->error(__('An alias was previously found.'), $token);
|
||||
}
|
||||
$ret->alias = $token->value;
|
||||
$alias = 0;
|
||||
@ -251,7 +251,7 @@ class Expression extends Component
|
||||
// the column name we parsed is actually the table name
|
||||
// and the table name is actually a database name.
|
||||
if ((!empty($ret->database)) || ($dot)) {
|
||||
$parser->error('Unexpected dot.', $token);
|
||||
$parser->error(__('Unexpected dot.'), $token);
|
||||
}
|
||||
$ret->database = $ret->table;
|
||||
$ret->table = $ret->column;
|
||||
@ -277,7 +277,8 @@ class Expression extends Component
|
||||
) {
|
||||
if (!empty($ret->alias)) {
|
||||
$parser->error(
|
||||
'An alias was previously found.', $token
|
||||
__('An alias was previously found.'),
|
||||
$token
|
||||
);
|
||||
}
|
||||
$ret->alias = $token->value;
|
||||
@ -296,7 +297,8 @@ class Expression extends Component
|
||||
) {
|
||||
if (!empty($ret->alias)) {
|
||||
$parser->error(
|
||||
'An alias was previously found.', $token
|
||||
__('An alias was previously found.'),
|
||||
$token
|
||||
);
|
||||
}
|
||||
$ret->alias = $token->value;
|
||||
@ -318,7 +320,10 @@ class Expression extends Component
|
||||
}
|
||||
|
||||
if ($alias === 2) {
|
||||
$parser->error('An alias was expected.', $list->tokens[$list->idx - 1]);
|
||||
$parser->error(
|
||||
__('An alias was expected.'),
|
||||
$list->tokens[$list->idx - 1]
|
||||
);
|
||||
}
|
||||
|
||||
// Whitespaces might be added at the end.
|
||||
|
||||
@ -36,8 +36,6 @@ class ExpressionArray extends Component
|
||||
{
|
||||
$ret = array();
|
||||
|
||||
$expr = null;
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
@ -92,7 +90,7 @@ class ExpressionArray extends Component
|
||||
|
||||
if ($state === 0) {
|
||||
$parser->error(
|
||||
'An expression was expected.',
|
||||
__('An expression was expected.'),
|
||||
$list->tokens[$list->idx]
|
||||
);
|
||||
}
|
||||
|
||||
@ -198,7 +198,10 @@ class FieldDefinition extends Component
|
||||
if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) {
|
||||
$state = 1;
|
||||
} else {
|
||||
$parser->error('An opening bracket was expected.', $token);
|
||||
$parser->error(
|
||||
__('An opening bracket was expected.'),
|
||||
$token
|
||||
);
|
||||
break;
|
||||
}
|
||||
} elseif ($state === 1) {
|
||||
@ -248,7 +251,10 @@ class FieldDefinition extends Component
|
||||
}
|
||||
|
||||
if (($state !== 0) && ($state !== 6)) {
|
||||
$parser->error('A closing bracket was expected.', $list->tokens[$list->idx - 1]);
|
||||
$parser->error(
|
||||
__('A closing bracket was expected.'),
|
||||
$list->tokens[$list->idx - 1]
|
||||
);
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
|
||||
@ -87,7 +87,7 @@ class Limit extends Component
|
||||
|
||||
if (($token->type === Token::TYPE_KEYWORD) && ($token->value === 'OFFSET')) {
|
||||
if ($offset) {
|
||||
$parser->error('An offset was expected.', $token);
|
||||
$parser->error(__('An offset was expected.'), $token);
|
||||
}
|
||||
$offset = true;
|
||||
continue;
|
||||
@ -108,7 +108,10 @@ class Limit extends Component
|
||||
}
|
||||
|
||||
if ($offset) {
|
||||
$parser->error('An offset was expected.', $list->tokens[$list->idx - 1]);
|
||||
$parser->error(
|
||||
__('An offset was expected.'),
|
||||
$list->tokens[$list->idx - 1]
|
||||
);
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
|
||||
@ -137,7 +137,13 @@ class OptionsArray extends Component
|
||||
// real options (e.g. if there are 5 options, the first
|
||||
// fake ID is 6).
|
||||
if (isset($ret->options[$lastOptionId])) {
|
||||
$parser->error('This option conflicts with \'' . $ret->options[$lastOptionId] . '\'.', $token);
|
||||
$parser->error(
|
||||
sprintf(
|
||||
__('This option conflicts with "%1$s".'),
|
||||
$ret->options[$lastOptionId]
|
||||
),
|
||||
$token
|
||||
);
|
||||
$lastOptionId = $lastAssignedId++;
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -98,14 +98,20 @@ class RenameOperation extends Component
|
||||
)
|
||||
);
|
||||
if (empty($expr->old)) {
|
||||
$parser->error('The old name of the table was expected.', $token);
|
||||
$parser->error(
|
||||
__('The old name of the table was expected.'),
|
||||
$token
|
||||
);
|
||||
}
|
||||
$state = 1;
|
||||
} elseif ($state === 1) {
|
||||
if (($token->type === Token::TYPE_KEYWORD) && ($token->value === 'TO')) {
|
||||
$state = 2;
|
||||
} else {
|
||||
$parser->error('Keyword "TO" was expected.', $token);
|
||||
$parser->error(
|
||||
__('Keyword "TO" was expected.'),
|
||||
$token
|
||||
);
|
||||
break;
|
||||
}
|
||||
} elseif ($state === 2) {
|
||||
@ -119,7 +125,10 @@ class RenameOperation extends Component
|
||||
)
|
||||
);
|
||||
if (empty($expr->new)) {
|
||||
$parser->error('The new name of the table was expected.', $token);
|
||||
$parser->error(
|
||||
__('The new name of the table was expected.'),
|
||||
$token
|
||||
);
|
||||
}
|
||||
$state = 3;
|
||||
} elseif ($state === 3) {
|
||||
@ -134,7 +143,10 @@ class RenameOperation extends Component
|
||||
}
|
||||
|
||||
if ($state !== 3) {
|
||||
$parser->error('A rename operation was expected.', $list->tokens[$list->idx - 1]);
|
||||
$parser->error(
|
||||
__('A rename operation was expected.'),
|
||||
$list->tokens[$list->idx - 1]
|
||||
);
|
||||
}
|
||||
|
||||
// Last iteration was not saved.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -7,410 +7,436 @@
|
||||
*
|
||||
* @package SqlParser
|
||||
*/
|
||||
namespace SqlParser;
|
||||
|
||||
use SqlParser\Statements\SelectStatement;
|
||||
use SqlParser\Exceptions\ParserException;
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Takes multiple tokens (contained in a Lexer instance) as input and builds a
|
||||
* parse tree.
|
||||
*
|
||||
* @category Parser
|
||||
* @package SqlParser
|
||||
* @author Dan Ungureanu <udan1107@gmail.com>
|
||||
* @license http://opensource.org/licenses/GPL-2.0 GNU Public License
|
||||
*/
|
||||
class Parser
|
||||
{
|
||||
|
||||
/**
|
||||
* Array of classes that are used in parsing the SQL statements.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $STATEMENT_PARSERS = array(
|
||||
|
||||
'EXPLAIN' => 'SqlParser\\Statements\\ExplainStatement',
|
||||
|
||||
// Table Maintenance Statements
|
||||
// https://dev.mysql.com/doc/refman/5.7/en/table-maintenance-sql.html
|
||||
'ANALYZE' => 'SqlParser\\Statements\\AnalyzeStatement',
|
||||
'BACKUP' => 'SqlParser\\Statements\\BackupStatement',
|
||||
'CHECK' => 'SqlParser\\Statements\\CheckStatement',
|
||||
'CHECKSUM' => 'SqlParser\\Statements\\ChecksumStatement',
|
||||
'OPTIMIZE' => 'SqlParser\\Statements\\OptimizeStatement',
|
||||
'REPAIR' => 'SqlParser\\Statements\\RepairStatement',
|
||||
'RESTORE' => 'SqlParser\\Statements\\RestoreStatement',
|
||||
|
||||
// Database Administration Statements
|
||||
// https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-server-administration.html
|
||||
'SET' => '',
|
||||
'SHOW' => 'SqlParser\\Statements\\ShowStatement',
|
||||
|
||||
// Data Definition Statements.
|
||||
// https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-data-definition.html
|
||||
'ALTER' => 'SqlParser\\Statements\\AlterStatement',
|
||||
'CREATE' => 'SqlParser\\Statements\\CreateStatement',
|
||||
'DROP' => 'SqlParser\\Statements\\DropStatement',
|
||||
'RENAME' => 'SqlParser\\Statements\\RenameStatement',
|
||||
'TRUNCATE' => 'SqlParser\\Statements\\TruncateStatement',
|
||||
|
||||
// Data Manipulation Statements.
|
||||
// https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-data-manipulation.html
|
||||
'CALL' => 'SqlParser\\Statements\\CallStatement',
|
||||
'DELETE' => 'SqlParser\\Statements\\DeleteStatement',
|
||||
'DO' => '',
|
||||
'HANDLER' => '',
|
||||
'INSERT' => 'SqlParser\\Statements\\InsertStatement',
|
||||
'LOAD' => '',
|
||||
'REPLACE' => 'SqlParser\\Statements\\ReplaceStatement',
|
||||
'SELECT' => 'SqlParser\\Statements\\SelectStatement',
|
||||
'UPDATE' => 'SqlParser\\Statements\\UpdateStatement',
|
||||
|
||||
// Prepared Statements.
|
||||
// https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-prepared-statements.html
|
||||
'PREPARE' => '',
|
||||
'EXECUTE' => '',
|
||||
);
|
||||
|
||||
/**
|
||||
* Array of classes that are used in parsing SQL components.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $KEYWORD_PARSERS = array(
|
||||
|
||||
// This is not a proper keyword and was added here to help the builder.
|
||||
'_OPTIONS' => array(
|
||||
'class' => 'SqlParser\\Components\\OptionsArray',
|
||||
'field' => 'options',
|
||||
),
|
||||
|
||||
'ALTER' => array(
|
||||
'class' => 'SqlParser\\Components\\Expression',
|
||||
'field' => 'table',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'ANALYZE' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'tables',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'BACKUP' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'tables',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'CALL' => array(
|
||||
'class' => 'SqlParser\\Components\\FunctionCall',
|
||||
'field' => 'call',
|
||||
),
|
||||
'CHECK' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'tables',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'CHECKSUM' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'tables',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'DROP' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'fields',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'FROM' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'from',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'GROUP BY' => array(
|
||||
'class' => 'SqlParser\\Components\\OrderKeyword',
|
||||
'field' => 'group',
|
||||
),
|
||||
'HAVING' => array(
|
||||
'class' => 'SqlParser\\Components\\Condition',
|
||||
'field' => 'having',
|
||||
),
|
||||
'INTO' => array(
|
||||
'class' => 'SqlParser\\Components\\IntoKeyword',
|
||||
'field' => 'into',
|
||||
),
|
||||
'JOIN' => array(
|
||||
'class' => 'SqlParser\\Components\\JoinKeyword',
|
||||
'field' => 'join',
|
||||
),
|
||||
'LEFT JOIN' => array(
|
||||
'class' => 'SqlParser\\Components\\JoinKeyword',
|
||||
'field' => 'join',
|
||||
),
|
||||
'RIGHT JOIN' => array(
|
||||
'class' => 'SqlParser\\Components\\JoinKeyword',
|
||||
'field' => 'join',
|
||||
),
|
||||
'INNER JOIN' => array(
|
||||
'class' => 'SqlParser\\Components\\JoinKeyword',
|
||||
'field' => 'join',
|
||||
),
|
||||
'FULL JOIN' => array(
|
||||
'class' => 'SqlParser\\Components\\JoinKeyword',
|
||||
'field' => 'join',
|
||||
),
|
||||
'LIMIT' => array(
|
||||
'class' => 'SqlParser\\Components\\Limit',
|
||||
'field' => 'limit',
|
||||
),
|
||||
'OPTIMIZE' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'tables',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'ORDER BY' => array(
|
||||
'class' => 'SqlParser\\Components\\OrderKeyword',
|
||||
'field' => 'order',
|
||||
),
|
||||
'PARTITION' => array(
|
||||
'class' => 'SqlParser\\Components\\ArrayObj',
|
||||
'field' => 'partition',
|
||||
),
|
||||
'PROCEDURE' => array(
|
||||
'class' => 'SqlParser\\Components\\FunctionCall',
|
||||
'field' => 'procedure',
|
||||
),
|
||||
'RENAME' => array(
|
||||
'class' => 'SqlParser\\Components\\RenameOperation',
|
||||
'field' => 'renames',
|
||||
),
|
||||
'REPAIR' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'tables',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'RESTORE' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'tables',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'SET' => array(
|
||||
'class' => 'SqlParser\\Components\\SetOperation',
|
||||
'field' => 'set',
|
||||
),
|
||||
'SELECT' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'expr',
|
||||
),
|
||||
'TRUNCATE' => array(
|
||||
'class' => 'SqlParser\\Components\\Expression',
|
||||
'field' => 'table',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'UPDATE' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'tables',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'VALUE' => array(
|
||||
'class' => 'SqlParser\\Components\\Array2d',
|
||||
'field' => 'values',
|
||||
),
|
||||
'VALUES' => array(
|
||||
'class' => 'SqlParser\\Components\\Array2d',
|
||||
'field' => 'values',
|
||||
),
|
||||
'WHERE' => array(
|
||||
'class' => 'SqlParser\\Components\\Condition',
|
||||
'field' => 'where',
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
/**
|
||||
* The list of tokens that are parsed.
|
||||
*
|
||||
* @var TokensList
|
||||
*/
|
||||
public $list;
|
||||
|
||||
/**
|
||||
* Whether errors should throw exceptions or just be stored.
|
||||
*
|
||||
* @var bool
|
||||
*
|
||||
* @see static::$errors
|
||||
*/
|
||||
public $strict = false;
|
||||
|
||||
/**
|
||||
* List of errors that occurred during parsing.
|
||||
*
|
||||
* Usually, the parsing does not stop once an error occurred because that
|
||||
* error might be a false positive or a partial result (even a bad one)
|
||||
* might be needed.
|
||||
*
|
||||
* @var ParserException[]
|
||||
*
|
||||
* @see Parser::error()
|
||||
*/
|
||||
public $errors = array();
|
||||
|
||||
/**
|
||||
* List of statements parsed.
|
||||
*
|
||||
* @var Statement[]
|
||||
*/
|
||||
public $statements = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param mixed $list The list of tokens to be parsed.
|
||||
* @param bool $strict Whether strict mode should be enabled or not.
|
||||
*/
|
||||
public function __construct($list = null, $strict = false)
|
||||
{
|
||||
if ((is_string($list)) || ($list instanceof UtfString)) {
|
||||
$lexer = new Lexer($list, $strict);
|
||||
$this->list = $lexer->list;
|
||||
} elseif ($list instanceof TokensList) {
|
||||
$this->list = $list;
|
||||
}
|
||||
|
||||
$this->strict = $strict;
|
||||
|
||||
if ($list !== null) {
|
||||
$this->parse();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the parse trees.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function parse()
|
||||
{
|
||||
if (!function_exists('__')) {
|
||||
|
||||
/**
|
||||
* Last parsed statement.
|
||||
* @var Statement $lastStatement
|
||||
* Translates the given string.
|
||||
*
|
||||
* @param string $str String to be translated.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
$lastStatement = null;
|
||||
|
||||
/**
|
||||
* Whether a union is parsed or not.
|
||||
* @var bool $inUnion
|
||||
*/
|
||||
$inUnion = true;
|
||||
|
||||
/**
|
||||
* The index of the last token from the last statement.
|
||||
* @var int $prevLastIdx
|
||||
*/
|
||||
$prevLastIdx = -1;
|
||||
|
||||
/**
|
||||
* The list of tokens.
|
||||
* @var TokensList $list
|
||||
*/
|
||||
$list = &$this->list;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
* @var Token $token
|
||||
*/
|
||||
$token = $list->tokens[$list->idx];
|
||||
|
||||
// Statements can start with keywords only.
|
||||
// Comments, whitespaces, etc. are ignored.
|
||||
if ($token->type !== Token::TYPE_KEYWORD) {
|
||||
if (($token->type !== TOKEN::TYPE_COMMENT)
|
||||
&& ($token->type !== Token::TYPE_WHITESPACE)
|
||||
&& ($token->type !== Token::TYPE_OPERATOR) // `(` and `)`
|
||||
&& ($token->type !== Token::TYPE_DELIMITER)
|
||||
) {
|
||||
$this->error('Unexpected beginning of statement.', $token);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($token->value === 'UNION') {
|
||||
$inUnion = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Checking if it is a known statement that can be parsed.
|
||||
if (empty(static::$STATEMENT_PARSERS[$token->value])) {
|
||||
$this->error('Unrecognized statement type.', $token);
|
||||
// Skipping to the end of this statement.
|
||||
$list->getNextOfType(Token::TYPE_DELIMITER);
|
||||
//
|
||||
$prevLastIdx = $list->idx;
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the class that is used for parsing.
|
||||
* @var string $class
|
||||
*/
|
||||
$class = static::$STATEMENT_PARSERS[$token->value];
|
||||
|
||||
/**
|
||||
* Processed statement.
|
||||
* @var Statement $statement
|
||||
*/
|
||||
$statement = new $class($this, $this->list);
|
||||
|
||||
// The first token that is a part of this token is the next token
|
||||
// unprocessed by the previous statement.
|
||||
// There might be brackets around statements and this shouldn't
|
||||
// affect the parser
|
||||
$statement->first = $prevLastIdx + 1;
|
||||
|
||||
// Storing the index of the last token parsed and updating the old
|
||||
// index.
|
||||
$statement->last = $list->idx;
|
||||
$prevLastIdx = $list->idx;
|
||||
|
||||
// Finally, storing the statement.
|
||||
if (($inUnion)
|
||||
&& ($lastStatement instanceof SelectStatement)
|
||||
&& ($statement instanceof SelectStatement)
|
||||
) {
|
||||
/**
|
||||
* Last SELECT statement.
|
||||
* @var SelectStatement $lastStatement
|
||||
*/
|
||||
$lastStatement->union[] = $statement;
|
||||
$inUnion = false;
|
||||
} else {
|
||||
$this->statements[] = $statement;
|
||||
$lastStatement = $statement;
|
||||
}
|
||||
|
||||
function __($str)
|
||||
{
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace SqlParser {
|
||||
|
||||
use SqlParser\Statements\SelectStatement;
|
||||
use SqlParser\Exceptions\ParserException;
|
||||
|
||||
/**
|
||||
* Takes multiple tokens (contained in a Lexer instance) as input and builds a
|
||||
* parse tree.
|
||||
*
|
||||
* @category Parser
|
||||
* @package SqlParser
|
||||
* @author Dan Ungureanu <udan1107@gmail.com>
|
||||
* @license http://opensource.org/licenses/GPL-2.0 GNU Public License
|
||||
*/
|
||||
class Parser
|
||||
{
|
||||
|
||||
/**
|
||||
* Array of classes that are used in parsing the SQL statements.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $STATEMENT_PARSERS = array(
|
||||
|
||||
'EXPLAIN' => 'SqlParser\\Statements\\ExplainStatement',
|
||||
|
||||
// Table Maintenance Statements
|
||||
// https://dev.mysql.com/doc/refman/5.7/en/table-maintenance-sql.html
|
||||
'ANALYZE' => 'SqlParser\\Statements\\AnalyzeStatement',
|
||||
'BACKUP' => 'SqlParser\\Statements\\BackupStatement',
|
||||
'CHECK' => 'SqlParser\\Statements\\CheckStatement',
|
||||
'CHECKSUM' => 'SqlParser\\Statements\\ChecksumStatement',
|
||||
'OPTIMIZE' => 'SqlParser\\Statements\\OptimizeStatement',
|
||||
'REPAIR' => 'SqlParser\\Statements\\RepairStatement',
|
||||
'RESTORE' => 'SqlParser\\Statements\\RestoreStatement',
|
||||
|
||||
// Database Administration Statements
|
||||
// https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-server-administration.html
|
||||
'SET' => '',
|
||||
'SHOW' => 'SqlParser\\Statements\\ShowStatement',
|
||||
|
||||
// Data Definition Statements.
|
||||
// https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-data-definition.html
|
||||
'ALTER' => 'SqlParser\\Statements\\AlterStatement',
|
||||
'CREATE' => 'SqlParser\\Statements\\CreateStatement',
|
||||
'DROP' => 'SqlParser\\Statements\\DropStatement',
|
||||
'RENAME' => 'SqlParser\\Statements\\RenameStatement',
|
||||
'TRUNCATE' => 'SqlParser\\Statements\\TruncateStatement',
|
||||
|
||||
// Data Manipulation Statements.
|
||||
// https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-data-manipulation.html
|
||||
'CALL' => 'SqlParser\\Statements\\CallStatement',
|
||||
'DELETE' => 'SqlParser\\Statements\\DeleteStatement',
|
||||
'DO' => '',
|
||||
'HANDLER' => '',
|
||||
'INSERT' => 'SqlParser\\Statements\\InsertStatement',
|
||||
'LOAD' => '',
|
||||
'REPLACE' => 'SqlParser\\Statements\\ReplaceStatement',
|
||||
'SELECT' => 'SqlParser\\Statements\\SelectStatement',
|
||||
'UPDATE' => 'SqlParser\\Statements\\UpdateStatement',
|
||||
|
||||
// Prepared Statements.
|
||||
// https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-prepared-statements.html
|
||||
'PREPARE' => '',
|
||||
'EXECUTE' => '',
|
||||
);
|
||||
|
||||
/**
|
||||
* Array of classes that are used in parsing SQL components.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $KEYWORD_PARSERS = array(
|
||||
|
||||
// This is not a proper keyword and was added here to help the builder.
|
||||
'_OPTIONS' => array(
|
||||
'class' => 'SqlParser\\Components\\OptionsArray',
|
||||
'field' => 'options',
|
||||
),
|
||||
|
||||
'ALTER' => array(
|
||||
'class' => 'SqlParser\\Components\\Expression',
|
||||
'field' => 'table',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'ANALYZE' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'tables',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'BACKUP' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'tables',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'CALL' => array(
|
||||
'class' => 'SqlParser\\Components\\FunctionCall',
|
||||
'field' => 'call',
|
||||
),
|
||||
'CHECK' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'tables',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'CHECKSUM' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'tables',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'DROP' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'fields',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'FROM' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'from',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'GROUP BY' => array(
|
||||
'class' => 'SqlParser\\Components\\OrderKeyword',
|
||||
'field' => 'group',
|
||||
),
|
||||
'HAVING' => array(
|
||||
'class' => 'SqlParser\\Components\\Condition',
|
||||
'field' => 'having',
|
||||
),
|
||||
'INTO' => array(
|
||||
'class' => 'SqlParser\\Components\\IntoKeyword',
|
||||
'field' => 'into',
|
||||
),
|
||||
'JOIN' => array(
|
||||
'class' => 'SqlParser\\Components\\JoinKeyword',
|
||||
'field' => 'join',
|
||||
),
|
||||
'LEFT JOIN' => array(
|
||||
'class' => 'SqlParser\\Components\\JoinKeyword',
|
||||
'field' => 'join',
|
||||
),
|
||||
'RIGHT JOIN' => array(
|
||||
'class' => 'SqlParser\\Components\\JoinKeyword',
|
||||
'field' => 'join',
|
||||
),
|
||||
'INNER JOIN' => array(
|
||||
'class' => 'SqlParser\\Components\\JoinKeyword',
|
||||
'field' => 'join',
|
||||
),
|
||||
'FULL JOIN' => array(
|
||||
'class' => 'SqlParser\\Components\\JoinKeyword',
|
||||
'field' => 'join',
|
||||
),
|
||||
'LIMIT' => array(
|
||||
'class' => 'SqlParser\\Components\\Limit',
|
||||
'field' => 'limit',
|
||||
),
|
||||
'OPTIMIZE' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'tables',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'ORDER BY' => array(
|
||||
'class' => 'SqlParser\\Components\\OrderKeyword',
|
||||
'field' => 'order',
|
||||
),
|
||||
'PARTITION' => array(
|
||||
'class' => 'SqlParser\\Components\\ArrayObj',
|
||||
'field' => 'partition',
|
||||
),
|
||||
'PROCEDURE' => array(
|
||||
'class' => 'SqlParser\\Components\\FunctionCall',
|
||||
'field' => 'procedure',
|
||||
),
|
||||
'RENAME' => array(
|
||||
'class' => 'SqlParser\\Components\\RenameOperation',
|
||||
'field' => 'renames',
|
||||
),
|
||||
'REPAIR' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'tables',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'RESTORE' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'tables',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'SET' => array(
|
||||
'class' => 'SqlParser\\Components\\SetOperation',
|
||||
'field' => 'set',
|
||||
),
|
||||
'SELECT' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'expr',
|
||||
),
|
||||
'TRUNCATE' => array(
|
||||
'class' => 'SqlParser\\Components\\Expression',
|
||||
'field' => 'table',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'UPDATE' => array(
|
||||
'class' => 'SqlParser\\Components\\ExpressionArray',
|
||||
'field' => 'tables',
|
||||
'options' => array('skipColumn' => true),
|
||||
),
|
||||
'VALUE' => array(
|
||||
'class' => 'SqlParser\\Components\\Array2d',
|
||||
'field' => 'values',
|
||||
),
|
||||
'VALUES' => array(
|
||||
'class' => 'SqlParser\\Components\\Array2d',
|
||||
'field' => 'values',
|
||||
),
|
||||
'WHERE' => array(
|
||||
'class' => 'SqlParser\\Components\\Condition',
|
||||
'field' => 'where',
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
/**
|
||||
* The list of tokens that are parsed.
|
||||
*
|
||||
* @var TokensList
|
||||
*/
|
||||
public $list;
|
||||
|
||||
/**
|
||||
* Whether errors should throw exceptions or just be stored.
|
||||
*
|
||||
* @var bool
|
||||
*
|
||||
* @see static::$errors
|
||||
*/
|
||||
public $strict = false;
|
||||
|
||||
/**
|
||||
* List of errors that occurred during parsing.
|
||||
*
|
||||
* Usually, the parsing does not stop once an error occurred because that
|
||||
* error might be a false positive or a partial result (even a bad one)
|
||||
* might be needed.
|
||||
*
|
||||
* @var ParserException[]
|
||||
*
|
||||
* @see Parser::error()
|
||||
*/
|
||||
public $errors = array();
|
||||
|
||||
/**
|
||||
* List of statements parsed.
|
||||
*
|
||||
* @var Statement[]
|
||||
*/
|
||||
public $statements = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param mixed $list The list of tokens to be parsed.
|
||||
* @param bool $strict Whether strict mode should be enabled or not.
|
||||
*/
|
||||
public function __construct($list = null, $strict = false)
|
||||
{
|
||||
if ((is_string($list)) || ($list instanceof UtfString)) {
|
||||
$lexer = new Lexer($list, $strict);
|
||||
$this->list = $lexer->list;
|
||||
} elseif ($list instanceof TokensList) {
|
||||
$this->list = $list;
|
||||
}
|
||||
|
||||
$this->strict = $strict;
|
||||
|
||||
if ($list !== null) {
|
||||
$this->parse();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the parse trees.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function parse()
|
||||
{
|
||||
|
||||
/**
|
||||
* Last parsed statement.
|
||||
* @var Statement $lastStatement
|
||||
*/
|
||||
$lastStatement = null;
|
||||
|
||||
/**
|
||||
* Whether a union is parsed or not.
|
||||
* @var bool $inUnion
|
||||
*/
|
||||
$inUnion = true;
|
||||
|
||||
/**
|
||||
* The index of the last token from the last statement.
|
||||
* @var int $prevLastIdx
|
||||
*/
|
||||
$prevLastIdx = -1;
|
||||
|
||||
/**
|
||||
* The list of tokens.
|
||||
* @var TokensList $list
|
||||
*/
|
||||
$list = &$this->list;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
* @var Token $token
|
||||
*/
|
||||
$token = $list->tokens[$list->idx];
|
||||
|
||||
// Statements can start with keywords only.
|
||||
// Comments, whitespaces, etc. are ignored.
|
||||
if ($token->type !== Token::TYPE_KEYWORD) {
|
||||
if (($token->type !== TOKEN::TYPE_COMMENT)
|
||||
&& ($token->type !== Token::TYPE_WHITESPACE)
|
||||
&& ($token->type !== Token::TYPE_OPERATOR) // `(` and `)`
|
||||
&& ($token->type !== Token::TYPE_DELIMITER)
|
||||
) {
|
||||
$this->error(
|
||||
__('Unexpected beginning of statement.'),
|
||||
$token
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($token->value === 'UNION') {
|
||||
$inUnion = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Checking if it is a known statement that can be parsed.
|
||||
if (empty(static::$STATEMENT_PARSERS[$token->value])) {
|
||||
$this->error(
|
||||
__('Unrecognized statement type.'),
|
||||
$token
|
||||
);
|
||||
// Skipping to the end of this statement.
|
||||
$list->getNextOfType(Token::TYPE_DELIMITER);
|
||||
//
|
||||
$prevLastIdx = $list->idx;
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the class that is used for parsing.
|
||||
* @var string $class
|
||||
*/
|
||||
$class = static::$STATEMENT_PARSERS[$token->value];
|
||||
|
||||
/**
|
||||
* Processed statement.
|
||||
* @var Statement $statement
|
||||
*/
|
||||
$statement = new $class($this, $this->list);
|
||||
|
||||
// The first token that is a part of this token is the next token
|
||||
// unprocessed by the previous statement.
|
||||
// There might be brackets around statements and this shouldn't
|
||||
// affect the parser
|
||||
$statement->first = $prevLastIdx + 1;
|
||||
|
||||
// Storing the index of the last token parsed and updating the old
|
||||
// index.
|
||||
$statement->last = $list->idx;
|
||||
$prevLastIdx = $list->idx;
|
||||
|
||||
// Finally, storing the statement.
|
||||
if (($inUnion)
|
||||
&& ($lastStatement instanceof SelectStatement)
|
||||
&& ($statement instanceof SelectStatement)
|
||||
) {
|
||||
/**
|
||||
* Last SELECT statement.
|
||||
* @var SelectStatement $lastStatement
|
||||
*/
|
||||
$lastStatement->union[] = $statement;
|
||||
$inUnion = false;
|
||||
} else {
|
||||
$this->statements[] = $statement;
|
||||
$lastStatement = $statement;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new error log.
|
||||
*
|
||||
* @param string $msg The error message.
|
||||
* @param Token $token The token that produced the error.
|
||||
* @param int $code The code of the error.
|
||||
*
|
||||
* @throws ParserException Throws the exception, if strict mode is enabled.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function error($msg = '', Token $token = null, $code = 0)
|
||||
{
|
||||
$error = new ParserException($msg, $token, $code);
|
||||
if ($this->strict) {
|
||||
throw $error;
|
||||
}
|
||||
$this->errors[] = $error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new error log.
|
||||
*
|
||||
* @param string $msg The error message.
|
||||
* @param Token $token The token that produced the error.
|
||||
* @param int $code The code of the error.
|
||||
*
|
||||
* @throws ParserException Throws the exception, if strict mode is enabled.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function error($msg = '', Token $token = null, $code = 0)
|
||||
{
|
||||
$error = new ParserException($msg, $token, $code);
|
||||
if ($this->strict) {
|
||||
throw $error;
|
||||
}
|
||||
$this->errors[] = $error;
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,7 +198,7 @@ abstract class Statement
|
||||
if (($token->type !== TOKEN::TYPE_COMMENT)
|
||||
&& ($token->type !== Token::TYPE_WHITESPACE)
|
||||
) {
|
||||
$parser->error('Unexpected token.', $token);
|
||||
$parser->error(__('Unexpected token.'), $token);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@ -248,10 +248,7 @@ abstract class Statement
|
||||
} elseif ($class === null) {
|
||||
// There is no parser for this keyword and isn't the beginning
|
||||
// of a statement (so no options) either.
|
||||
$parser->error(
|
||||
'Unrecognized keyword.',
|
||||
$token
|
||||
);
|
||||
$parser->error(__('Unrecognized keyword.'), $token);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@ -290,7 +290,7 @@ class CreateStatement extends Statement
|
||||
|
||||
if (empty($this->name)) {
|
||||
$parser->error(
|
||||
'The name of the entity was expected.',
|
||||
__('The name of the entity was expected.'),
|
||||
$list->tokens[$list->idx]
|
||||
);
|
||||
} else {
|
||||
@ -307,7 +307,7 @@ class CreateStatement extends Statement
|
||||
$this->fields = FieldDefinition::parse($parser, $list);
|
||||
if (empty($this->fields)) {
|
||||
$parser->error(
|
||||
'At least one field definition was expected.',
|
||||
__('At least one field definition was expected.'),
|
||||
$list->tokens[$list->idx]
|
||||
);
|
||||
}
|
||||
@ -326,7 +326,7 @@ class CreateStatement extends Statement
|
||||
$token = $list->getNextOfType(Token::TYPE_KEYWORD);
|
||||
if ($token->value !== 'RETURNS') {
|
||||
$parser->error(
|
||||
'A \'RETURNS\' keyword was expected.',
|
||||
__('A "RETURNS" keyword was expected.'),
|
||||
$token
|
||||
);
|
||||
} else {
|
||||
|
||||
39
lint.php
39
lint.php
@ -8,31 +8,32 @@
|
||||
|
||||
define('PHPMYADMIN', true);
|
||||
|
||||
// We load the minimum files required to check if the user is logged in.
|
||||
require_once 'libraries/core.lib.php';
|
||||
require_once 'libraries/Config.class.php';
|
||||
$GLOBALS['PMA_Config'] = new PMA_Config(CONFIG_FILE);
|
||||
require_once 'libraries/session.inc.php';
|
||||
|
||||
// If user is not logged in, he should not send any requests, so we exit here to
|
||||
// avoid external requests.
|
||||
if (empty($_SESSION['encryption_key'])) {
|
||||
// Unauthorized access detected.
|
||||
exit;
|
||||
}
|
||||
/**
|
||||
* The SQL query to be analyzed.
|
||||
*
|
||||
* This does not need to be checked again XSS or MySQL injections because it is
|
||||
* never executed, just parsed.
|
||||
*
|
||||
* The client, which will recieve the JSON response will decode the message and
|
||||
* and any HTML fragments that are displayed to the user will be encoded anyway.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
$sql_query = $_REQUEST['sql_query'];
|
||||
|
||||
/**
|
||||
* Loads the SQL lexer and parser, which are used to detect errors.
|
||||
* Loading common files. Used to check for authorization, localization and to
|
||||
* load the parsing library.
|
||||
*/
|
||||
require_once 'libraries/sql-parser/autoload.php';
|
||||
require_once 'libraries/common.inc.php';
|
||||
|
||||
/**
|
||||
* Loads the linter.
|
||||
*/
|
||||
require_once 'libraries/Linter.class.php';
|
||||
|
||||
// The input of this function does not need to be checked again XSS or MySQL
|
||||
// injections because it is never executed, just parsed.
|
||||
// The client, which will recieve the JSON response will decode the message and
|
||||
// and any HTML fragments that are displayed to the user will be encoded anyway.
|
||||
PMA_Linter::lint($_REQUEST['sql_query']);
|
||||
// Disabling standard response.
|
||||
$response = PMA_Response::getInstance();
|
||||
$response->disable();
|
||||
|
||||
echo json_encode(PMA_Linter::lint($sql_query));
|
||||
|
||||
@ -74,8 +74,7 @@ class PMA_Linter_Test extends PHPUnit_Framework_TestCase
|
||||
*/
|
||||
public function testLintEmpty()
|
||||
{
|
||||
$this->expectOutputString('[]');
|
||||
PMA_Linter::lint('');
|
||||
$this->assertEquals(array(), PMA_Linter::lint(''));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,8 +84,7 @@ class PMA_Linter_Test extends PHPUnit_Framework_TestCase
|
||||
*/
|
||||
public function testLintNoErrors()
|
||||
{
|
||||
$this->expectOutputString('[]');
|
||||
PMA_Linter::lint('SELECT * FROM tbl');
|
||||
$this->assertEquals(array(), PMA_Linter::lint('SELECT * FROM tbl'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,29 +94,27 @@ class PMA_Linter_Test extends PHPUnit_Framework_TestCase
|
||||
*/
|
||||
public function testLintErrors()
|
||||
{
|
||||
$this->expectOutputString(
|
||||
json_encode(
|
||||
$this->assertEquals(
|
||||
array(
|
||||
array(
|
||||
array(
|
||||
'message' => 'Unrecognized data type. (near <code>IN</code>)',
|
||||
'fromLine' => 0,
|
||||
'fromColumn' => 22,
|
||||
'toLine' => 0,
|
||||
'toColumn' => 24,
|
||||
'severity' => 'error',
|
||||
),
|
||||
array(
|
||||
'message' => 'A closing bracket was expected. (near <code>IN</code>)',
|
||||
'fromLine' => 0,
|
||||
'fromColumn' => 22,
|
||||
'toLine' => 0,
|
||||
'toColumn' => 24,
|
||||
'severity' => 'error',
|
||||
)
|
||||
'message' => 'Unrecognized data type. (near <code>IN</code>)',
|
||||
'fromLine' => 0,
|
||||
'fromColumn' => 22,
|
||||
'toLine' => 0,
|
||||
'toColumn' => 24,
|
||||
'severity' => 'error',
|
||||
),
|
||||
array(
|
||||
'message' => 'A closing bracket was expected. (near <code>IN</code>)',
|
||||
'fromLine' => 0,
|
||||
'fromColumn' => 22,
|
||||
'toLine' => 0,
|
||||
'toColumn' => 24,
|
||||
'severity' => 'error',
|
||||
)
|
||||
)
|
||||
),
|
||||
PMA_Linter::lint('CREATE TABLE tbl ( id IN')
|
||||
);
|
||||
PMA_Linter::lint('CREATE TABLE tbl ( id IN');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -128,20 +124,18 @@ class PMA_Linter_Test extends PHPUnit_Framework_TestCase
|
||||
*/
|
||||
public function testLongQuery()
|
||||
{
|
||||
$this->expectOutputString(
|
||||
json_encode(
|
||||
$this->assertEquals(
|
||||
array(
|
||||
array(
|
||||
array(
|
||||
'message' => 'Linting is disabled for this query because it exceeds the maximum length.',
|
||||
'fromLine' => 0,
|
||||
'fromColumn' => 0,
|
||||
'toLine' => 0,
|
||||
'toColumn' => 0,
|
||||
'severity' => 'warning',
|
||||
)
|
||||
'message' => 'Linting is disabled for this query because it exceeds the maximum length.',
|
||||
'fromLine' => 0,
|
||||
'fromColumn' => 0,
|
||||
'toLine' => 0,
|
||||
'toColumn' => 0,
|
||||
'severity' => 'warning',
|
||||
)
|
||||
)
|
||||
),
|
||||
PMA_Linter::lint(str_repeat(";", 10001))
|
||||
);
|
||||
PMA_Linter::lint(str_repeat(";", 10001));
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user