Skip to content

Supports SHOW INDEX syntax #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/FakePdoStatementTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,17 @@ function ($row) {

break;

case Query\ShowIndexQuery::class:
$this->result = self::processResult(
$this->conn,
Processor\ShowIndexProcessor::process(
$this->conn,
new Processor\Scope(array_merge($params ?? [], $this->boundValues)),
$parsed_query
)
);
break;

default:
throw new \UnexpectedValueException('Unsupported operation type ' . $sql);
}
Expand Down
12 changes: 6 additions & 6 deletions src/Parser/SQLParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@
namespace Vimeo\MysqlEngine\Parser;

use Vimeo\MysqlEngine\TokenType;
use Vimeo\MysqlEngine\Query\{
SelectQuery,
use Vimeo\MysqlEngine\Query\{SelectQuery,
DeleteQuery,
ShowIndexQuery,
TruncateQuery,
InsertQuery,
UpdateQuery,
DropTableQuery,
ShowTablesQuery
};
ShowTablesQuery};

final class SQLParser
{
Expand Down Expand Up @@ -142,10 +141,11 @@ final class SQLParser
'TABLES' => true,
];

/** @var array<SelectQuery|InsertQuery|UpdateQuery|TruncateQuery|DeleteQuery|DropTableQuery|ShowTablesQuery|ShowIndexQuery> */
private static $cache = [];

/**
* @return SelectQuery|InsertQuery|UpdateQuery|TruncateQuery|DeleteQuery|DropTableQuery|ShowTablesQuery
* @return SelectQuery|InsertQuery|UpdateQuery|TruncateQuery|DeleteQuery|DropTableQuery|ShowTablesQuery|ShowIndexQuery
*/
public static function parse(string $sql)
{
Expand All @@ -157,7 +157,7 @@ public static function parse(string $sql)
}

/**
* @return SelectQuery|InsertQuery|UpdateQuery|TruncateQuery|DeleteQuery|DropTableQuery|ShowTablesQuery
* @return SelectQuery|InsertQuery|UpdateQuery|TruncateQuery|DeleteQuery|DropTableQuery|ShowTablesQuery|ShowIndexQuery
*/
private static function parseImpl(string $sql)
{
Expand Down
50 changes: 47 additions & 3 deletions src/Parser/ShowParser.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?php

namespace Vimeo\MysqlEngine\Parser;

use Vimeo\MysqlEngine\Query\ShowIndexQuery;
use Vimeo\MysqlEngine\TokenType;
use Vimeo\MysqlEngine\Query\ShowTablesQuery;

Expand Down Expand Up @@ -33,18 +35,33 @@ public function __construct(array $tokens, string $sql)
$this->sql = $sql;
}

public function parse() : ShowTablesQuery
/**
* @return ShowTablesQuery|ShowIndexQuery
* @throws ParserException
*/
public function parse()
{
if ($this->tokens[$this->pointer]->value !== 'SHOW') {
throw new ParserException("Parser error: expected SHOW");
}

$this->pointer++;

if ($this->tokens[$this->pointer]->value !== 'TABLES') {
throw new ParserException("Parser error: expected SHOW TABLES");
switch ($this->tokens[$this->pointer]->value) {
case 'TABLES':
return $this->parseShowTables();
case 'INDEX':
case 'INDEXES':
case 'KEYS':
return $this->parseShowIndex();
default:
throw new ParserException("Parser error: expected SHOW TABLES");
}

}

private function parseShowTables(): ShowTablesQuery
{
$this->pointer++;

if ($this->tokens[$this->pointer]->value !== 'LIKE') {
Expand All @@ -61,4 +78,31 @@ public function parse() : ShowTablesQuery

return new ShowTablesQuery($token->value, $this->sql);
}

private function parseShowIndex(): ShowIndexQuery
{
$this->pointer++;

if ($this->tokens[$this->pointer]->value !== 'FROM') {
throw new ParserException("Parser error: expected SHOW INDEX FROM");
}
$this->pointer++;
$token = $this->tokens[$this->pointer];
if ($token->type !== TokenType::IDENTIFIER) {
throw new ParserException("Expected table name after FROM");
}

$query = new ShowIndexQuery($token->value, $this->sql);
$this->pointer++;

if ($this->pointer < count($this->tokens)) {
if ($this->tokens[$this->pointer]->value !== 'WHERE') {
throw new ParserException("Parser error: expected SHOW INDEX FROM [TABLE_NAME] WHERE");
}
$expression_parser = new ExpressionParser($this->tokens, $this->pointer);
list($this->pointer, $expression) = $expression_parser->buildWithPointer();
$query->whereClause = $expression;
}
return $query;
}
}
75 changes: 75 additions & 0 deletions src/Processor/ShowIndexProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php


namespace Vimeo\MysqlEngine\Processor;


use Vimeo\MysqlEngine\FakePdoInterface;
use Vimeo\MysqlEngine\Query\ShowIndexQuery;
use Vimeo\MysqlEngine\Schema\Column;
use function PHPUnit\Framework\assertIsArray;

class ShowIndexProcessor extends Processor
{
public static function process(
FakePdoInterface $conn,
Scope $scope,
ShowIndexQuery $stmt
): QueryResult {
[$database, $table] = Processor::parseTableName($conn, $stmt->table);
$table_definition = $conn->getServer()->getTableDefinition(
$database,
$table
);
if (!$table_definition) {
return new QueryResult([], []);
}
$columns = [
'Table' => new Column\Varchar(255),
'Non_unique' => new Column\TinyInt(true, 1),
'Key_name' => new Column\Varchar(255),
'Seq_in_index' => new Column\IntColumn(true, 4),
'Column_name' => new Column\Varchar(255),
'Collation' => new Column\Char(1),
'Cardinality' => new Column\IntColumn(true, 4),
'Sub_part' => new Column\IntColumn(true, 4),
'Packed' => new Column\TinyInt(true, 1),
'Null' => new Column\Varchar(3),
'Index_type' => new Column\Varchar(5),
'Comment' => new Column\Varchar(255),
'Index_comment' => new Column\Varchar(255)
];
$rows = [];
foreach ($table_definition->indexes as $name => $index) {
foreach ($index->columns as $i => $column) {
$rows[] = [
'Table' => $table_definition->name,
'Non_unique' => $index->type === 'INDEX' ? 1 : 0,
'Key_name' => $name,
'Seq_in_index' => 1 + (int) $i,
'Column_name' => $column,
// because Index does not have "direction" (in the $cols of CreateIndex)
'Collation' => null,
/*
* https://dev.mysql.com/doc/refman/8.0/en/analyze-table.html
* because ANALYZE TABLE is not implemented
*/
'Cardinality' => null,
// because Index does not have "length" (in the $cols of CreateIndex)
'Sub_part' => null,
// because PACK_KEYS is not implemented
'Packed' => null,
'Null' => $table_definition->columns[$column]->isNullable ? 'YES' : '',
// because Index does not have $mode (in the CreateIndex)
'Index_type' => null,
// because DISABLE KEYS is not implemented
'Comment' => '',
// because INDEX COMMENT is skipped in CREATE TABLE
'Index_comment' => ''
];
}
}
$result = self::applyWhere($conn, $scope, $stmt->whereClause, new QueryResult($rows, $columns));
return new QueryResult(array_merge($result->rows), $result->columns);
}
}
28 changes: 28 additions & 0 deletions src/Query/ShowIndexQuery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php
namespace Vimeo\MysqlEngine\Query;

use Vimeo\MysqlEngine\Query\Expression\Expression;

final class ShowIndexQuery
{
/**
* @var ?Expression
*/
public $whereClause = null;

/**
* @var string
*/
public $table;

/**
* @var string
*/
public $sql;

public function __construct(string $table, string $sql)
{
$this->table = $table;
$this->sql = $sql;
}
}
3 changes: 2 additions & 1 deletion src/Schema/Index.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ class Index
public $type;

/**
* @var array
* @var array<string>
*/
public $columns;

/**
* @param 'INDEX'|'UNIQUE'|'PRIMARY'|'FULLTEXT'|'SPATIAL' $type
* @param array<string> $columns
*/
public function __construct(
string $type,
Expand Down
63 changes: 63 additions & 0 deletions tests/ShowIndexParseTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

namespace Vimeo\MysqlEngine\Tests;

use PHPUnit\Framework\TestCase;
use Vimeo\MysqlEngine\Parser\SQLParser;
use Vimeo\MysqlEngine\Query\SelectQuery;
use Vimeo\MysqlEngine\Query\ShowIndexQuery;

class ShowIndexParseTest extends TestCase
{
public function testSimpleParse()
{
$query = 'SHOW INDEX FROM `foo`';

$show_query = SQLParser::parse($query);

$this->assertInstanceOf(ShowIndexQuery::class, $show_query);
$this->assertSame('foo', $show_query->table);
}

public function testIndexesParse()
{
$query = 'SHOW INDEXES FROM `foo`';

$show_query = SQLParser::parse($query);

$this->assertInstanceOf(ShowIndexQuery::class, $show_query);
$this->assertSame('foo', $show_query->table);
}

public function testKeysParse()
{
$query = 'SHOW KEYS FROM `foo`';

$show_query = SQLParser::parse($query);

$this->assertInstanceOf(ShowIndexQuery::class, $show_query);
$this->assertSame('foo', $show_query->table);
}

public function testParseInvalid()
{
$query = 'SHOW INDEX FROM `foo';

$this->expectException(\Vimeo\MysqlEngine\Parser\LexerException::class);

$select_query = \Vimeo\MysqlEngine\Parser\SQLParser::parse($query);

$this->assertInstanceOf(SelectQuery::class, $select_query);
}

public function testWhereParse()
{
$query = "SHOW INDEX FROM `foo` WHERE `Key_name` = 'PRIMARY'";

$show_query = SQLParser::parse($query);

$this->assertInstanceOf(ShowIndexQuery::class, $show_query);
$this->assertSame('foo', $show_query->table);
}

}