Skip to content

Commit

Permalink
Add handling of command SHOW FULL COLUMNS FOR {table_name}
Browse files Browse the repository at this point in the history
Handling multiple databases for create queries, then sql is CREATE TABLE `database`.`table` and in same script is `information_schema`.`table`
Fixing PDO::getAttribute() missing method
  • Loading branch information
Andrew Svirin committed Oct 5, 2021
1 parent a0005ec commit ad3113e
Show file tree
Hide file tree
Showing 7 changed files with 236 additions and 10 deletions.
23 changes: 20 additions & 3 deletions src/FakePdoStatementTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,18 @@ public function universalExecute(?array $params = null)
$create_queries = (new Parser\CreateTableParser())->parse($sql);

foreach ($create_queries as $create_query) {
if (strpos($create_query->name, '.')) {
list($databaseName, $tableName) = explode('.', $create_query->name, 2);
} else {
$databaseName = $this->conn->getDatabaseName();
$tableName = $create_query->name;
}
$this->conn->getServer()->addTableDefinition(
$this->conn->getDatabaseName(),
$create_query->name,
$databaseName,
$tableName,
Processor\CreateProcessor::makeTableDefinition(
$create_query,
$this->conn->getDatabaseName()
$databaseName
)
);
}
Expand Down Expand Up @@ -284,6 +290,17 @@ function ($row) {
);
break;

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

default:
throw new \UnexpectedValueException('Unsupported operation type ' . $sql);
}
Expand Down
10 changes: 10 additions & 0 deletions src/FakePdoTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ public function setAttribute($key, $value)
return true;
}

public function getAttribute($key)
{
switch ($key) {
case \PDO::ATTR_CASE:
$value = $this->lowercaseResultKeys ? \PDO::CASE_LOWER : \PDO::CASE_UPPER;
}

return $value;
}

public function getServer() : Server
{
return $this->server;
Expand Down
8 changes: 7 additions & 1 deletion src/Parser/CreateTableParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,13 @@ private static function parseCreateTable(array $tokens, string $sql) : CreateQue
\array_shift($tokens);
}

$t = \array_shift($tokens);
// Extract [{database}.]{table}
if ($tokens[1] === '.') {
$t = \array_shift($tokens) . \array_shift($tokens) . \array_shift($tokens);
} else {
$t = \array_shift($tokens);
}

$name = static::decodeIdentifier($t);

if (static::nextTokenIs($tokens, 'LIKE')) {
Expand Down
10 changes: 6 additions & 4 deletions src/Parser/SQLParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
InsertQuery,
UpdateQuery,
DropTableQuery,
ShowTablesQuery};
ShowTablesQuery,
ShowColumnsQuery
};

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

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

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

/**
* @return SelectQuery|InsertQuery|UpdateQuery|TruncateQuery|DeleteQuery|DropTableQuery|ShowTablesQuery|ShowIndexQuery
* @return SelectQuery|InsertQuery|UpdateQuery|TruncateQuery|DeleteQuery|DropTableQuery|ShowTablesQuery|ShowIndexQuery|ShowColumnsQuery
*/
private static function parseImpl(string $sql)
{
Expand Down
45 changes: 43 additions & 2 deletions src/Parser/ShowParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

namespace Vimeo\MysqlEngine\Parser;

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

/**
* Very limited parser for SHOW TABLES LIKE 'foo'
Expand Down Expand Up @@ -47,13 +48,21 @@ public function parse()

$this->pointer++;

// For case with TABLES and COLUMNS could be optinaly used argument FULL.
if ($this->tokens[$this->pointer]->value === 'FULL') {
$isFull = true;
$this->pointer++;
}

switch ($this->tokens[$this->pointer]->value) {
case 'TABLES':
return $this->parseShowTables();
case 'INDEX':
case 'INDEXES':
case 'KEYS':
return $this->parseShowIndex();
case 'COLUMNS':
return $this->parseShowColumns($isFull ?? false);
default:
throw new ParserException("Parser error: expected SHOW TABLES");
}
Expand All @@ -64,7 +73,7 @@ private function parseShowTables(): ShowTablesQuery
$this->pointer++;

if ($this->tokens[$this->pointer]->value !== 'LIKE') {
throw new ParserException("Parser error: expected SHOW TABLES LIKE");
throw new ParserException("Parser error: expected SHOW [FULL] TABLES LIKE");
}

$this->pointer++;
Expand Down Expand Up @@ -102,6 +111,38 @@ private function parseShowIndex(): ShowIndexQuery
list($this->pointer, $expression) = $expression_parser->buildWithPointer();
$query->whereClause = $expression;
}

return $query;
}

private function parseShowColumns(bool $isFull): ShowColumnsQuery
{
$this->pointer++;

if ($this->tokens[$this->pointer]->value !== 'FROM') {
throw new ParserException("Parser error: expected SHOW [FULL] COLUMNS FROM");
}

$this->pointer++;

$token = $this->tokens[$this->pointer];
if ($token->type !== TokenType::IDENTIFIER) {
throw new ParserException("Expected table name after FROM");
}

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

if ($this->pointer < count($this->tokens)) {
if ($this->tokens[$this->pointer]->value !== 'WHERE') {
throw new ParserException("Parser error: expected SHOW [FULL] COLUMNS FROM [TABLE_NAME] WHERE");
}
$expression_parser = new ExpressionParser($this->tokens, $this->pointer);
list($this->pointer, $expression) = $expression_parser->buildWithPointer();
$query->whereClause = $expression;
}

return $query;
}
}
116 changes: 116 additions & 0 deletions src/Processor/ShowColumnsProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php

namespace Vimeo\MysqlEngine\Processor;

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

class ShowColumnsProcessor extends Processor
{
public static function process(
FakePdoInterface $conn,
Scope $scope,
ShowColumnsQuery $stmt
): QueryResult {
[$database, $table] = Processor::parseTableName($conn, $stmt->table);
$table_definition = $conn->getServer()->getTableDefinition(
$database,
$table
);
if (!$table_definition) {
return new QueryResult([], []);
}
$columns = [
'Field' => new Column\Varchar(255),
'Type' => new Column\Varchar(255),
'Collation' => new Column\Varchar(255),
'Null' => new Column\Enum(['NO', 'YES']),
'Key' => new Column\Enum(['PRI']),
'Default' => new Column\Varchar(255),
'Extra' => new Column\Enum(['auto_increment']),
'Privilegies' => new Column\Varchar(255),
'Comment' => new Column\Varchar(255),
];
$rows = [];
foreach ($table_definition->columns as $name => $column) {
$rows[] = [
'Field' => $name,
'Type' => self::resolveType($column),
'Collation' => self::resolveCollation($column),
'Null' => $column->isNullable() ? 'YES' : 'NO',
'Key' => in_array($name, $table_definition->primaryKeyColumns) ? 'PRI' : '',
'Default' => $column->getDefault(),
'Extra' => self::resolveExtra($column),
'Privilegies' => 'select,insert,update,references',
'Comment' => '',
];
}
$result = self::applyWhere($conn, $scope, $stmt->whereClause, new QueryResult($rows, $columns));

$rows = array_merge($result->rows);
$columns = $result->columns;
if (!$stmt->isFull) {
$allowedColumns = [
'Field',
'Type',
'Null',
'Key',
'Default',
'Extra',
];
$columns = array_intersect_key($columns, array_flip($allowedColumns));
}

return new QueryResult(array_merge($result->rows), $result->columns);
}

private static function resolveType(Column $column): string
{
if ($column instanceof Column\Varchar) {
$type = 'varchar(255)';
} elseif ($column instanceof Column\IntColumn) {
$type = 'int(11)';
} elseif ($column instanceof Column\DateTime) {
$type = 'datetime';
} else {
throw new \UnexpectedValueException('Column type not specified.');
}

return $type;
}

private static function resolveCollation(Column $column): string
{
if (is_subclass_of($column, Column\CharacterColumn::class)) {
$collation = $column->getCollation();
}

return $collation ?? '';
}

private static function resolveKey(Column $column): string
{

}

private static function resolveDefault(Column $column): ?string
{
if ($column instanceof Column\Defaultable) {
$default = $column->getDefault();
}

return $default ?? null;
}

private static function resolveExtra(Column $column): string
{
if ($column instanceof Column\IntegerColumn) {
$extra = $column->isAutoIncrement() ? 'auto_increment' : '';
}

return $extra ?? '';
}
}
34 changes: 34 additions & 0 deletions src/Query/ShowColumnsQuery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace Vimeo\MysqlEngine\Query;

use Vimeo\MysqlEngine\Query\Expression\Expression;

final class ShowColumnsQuery
{
/**
* @var bool
*/
public $isFull = false;

/**
* @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;
}
}

0 comments on commit ad3113e

Please sign in to comment.