Skip to content

Commit 4baa186

Browse files
committed
feat: INSERT...SELECT syntax implementation.
1 parent 839b2fb commit 4baa186

File tree

5 files changed

+76
-4
lines changed

5 files changed

+76
-4
lines changed

src/Parser/InsertParser.php

+10-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
<?php
2+
23
namespace Vimeo\MysqlEngine\Parser;
34

45
use Vimeo\MysqlEngine\Query\Expression\Expression;
5-
use Vimeo\MysqlEngine\TokenType;
66
use Vimeo\MysqlEngine\Query\InsertQuery;
7+
use Vimeo\MysqlEngine\TokenType;
78

89
final class InsertParser
910
{
@@ -130,6 +131,12 @@ public function parse()
130131
list($this->pointer, $query->setClause) = $p->parse();
131132
break;
132133

134+
case 'SELECT':
135+
$select_parser = new SelectParser($this->pointer, $this->tokens, $this->sql);
136+
$query->selectQuery = $select_parser->parse();
137+
$this->pointer = $select_parser->getPointer();
138+
break;
139+
133140
default:
134141
throw new ParserException("Unexpected clause {$token->value}");
135142
}
@@ -168,7 +175,7 @@ public function parse()
168175
} else {
169176
if ($this->currentClause === 'COLUMN_LIST' && $needs_comma && $token->value === ')') {
170177
$needs_comma = false;
171-
if (($this->tokens[$this->pointer + 1]->value ?? null) !== 'VALUES') {
178+
if (!in_array($this->tokens[$this->pointer + 1]->value ?? null, ['VALUES', 'SELECT'])) {
172179
throw new ParserException("Expected VALUES after insert column list");
173180
}
174181
break;
@@ -203,7 +210,7 @@ public function parse()
203210

204211
$this->pointer++;
205212
}
206-
if ((!$query->insertColumns || !$query->values) && !$query->setClause) {
213+
if ((!$query->insertColumns || !$query->values) && !$query->setClause && !$query->selectQuery) {
207214
throw new ParserException("Missing values to insert");
208215
}
209216
return $query;

src/Parser/SelectParser.php

+6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?php
2+
23
namespace Vimeo\MysqlEngine\Parser;
34

45
use Vimeo\MysqlEngine\Query\Expression\ColumnExpression;
@@ -48,6 +49,11 @@ public function __construct(int $pointer, array $tokens, string $sql)
4849
$this->sql = $sql;
4950
}
5051

52+
public function getPointer(): int
53+
{
54+
return $this->pointer;
55+
}
56+
5157
public function parse() : SelectQuery
5258
{
5359
// if the first part of this query is nested, we should be able to unwrap it safely

src/Processor/InsertProcessor.php

+24-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<?php
2+
23
namespace Vimeo\MysqlEngine\Processor;
34

45
use Vimeo\MysqlEngine\DataIntegrity;
5-
use Vimeo\MysqlEngine\Query\Expression\ColumnExpression;
66
use Vimeo\MysqlEngine\Query\InsertQuery;
77
use Vimeo\MysqlEngine\Schema\Column\IntegerColumn;
88

@@ -108,6 +108,29 @@ public static function process(
108108
);
109109
}
110110

111+
if ($stmt->selectQuery) {
112+
$selectResult = SelectProcessor::process(
113+
$conn,
114+
$scope,
115+
$stmt->selectQuery
116+
)->rows;
117+
118+
$row = [];
119+
foreach ($selectResult as $value) {
120+
foreach ($stmt->selectQuery->selectExpressions as $index => $expr) {
121+
if (key_exists($index, $stmt->insertColumns)
122+
&& (is_string($value[$expr->name]) || is_int($value[$expr->name]))) {
123+
$row[$stmt->insertColumns[$index]] = $value[$expr->name];
124+
}
125+
}
126+
}
127+
128+
$table[] = $row;
129+
130+
$conn->getServer()->saveTable($database, $table_name, $table);
131+
return count($selectResult);
132+
}
133+
111134
return $rows_affected;
112135
}
113136
}

src/Query/InsertQuery.php

+4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?php
2+
23
namespace Vimeo\MysqlEngine\Query;
34

45
use Vimeo\MysqlEngine\Query\Expression\BinaryOperatorExpression;
@@ -41,6 +42,9 @@ final class InsertQuery
4142
*/
4243
public $setClause = null;
4344

45+
/** @var SelectQuery|null $selectExpression */
46+
public $selectQuery = null;
47+
4448
public function __construct(string $table, string $sql, bool $ignoreDupes)
4549
{
4650
$this->table = $table;

tests/EndToEndTest.php

+32
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php
22
namespace Vimeo\MysqlEngine\Tests;
33

4+
use PDO;
45
use PDOException;
56

67
class EndToEndTest extends \PHPUnit\Framework\TestCase
@@ -911,6 +912,37 @@ public function testInsertWithNullAndEmpty()
911912
);
912913
}
913914

915+
public function testInsertWithSelect(): void
916+
{
917+
$pdo = self::getConnectionToFullDB(false);
918+
919+
$query = $pdo->prepare(
920+
"INSERT INTO `video_game_characters`
921+
(`id`, `name`, `type`, `profession`, `console`, `is_alive`, `powerups`, `skills`, `created_on`)
922+
SELECT
923+
19+`id`, 'wario','villain','plumber','nes','1','3','{\"magic\":0, \"speed\":0, \"strength\":0, \"weapons\":0}', NOW(),
924+
FROM `video_game_characters`"
925+
);
926+
927+
$query->execute();
928+
929+
$query = $pdo->prepare(
930+
'SELECT `id`, `name`
931+
FROM `video_game_characters`
932+
ORDER BY `id` DESC
933+
LIMIT 1'
934+
);
935+
936+
$query->execute();
937+
938+
$this->assertSame(
939+
[
940+
['id' => 35, 'name' => 'wario'],
941+
],
942+
$query->fetchAll(PDO::FETCH_ASSOC)
943+
);
944+
}
945+
914946
public function testOrderBySecondDimensionAliased()
915947
{
916948
$pdo = self::getConnectionToFullDB(false);

0 commit comments

Comments
 (0)