Skip to content

Commit

Permalink
Fluent: add MERGE
Browse files Browse the repository at this point in the history
  • Loading branch information
forrest79 committed Jan 15, 2024
1 parent 2894822 commit 2e0b93c
Show file tree
Hide file tree
Showing 9 changed files with 318 additions and 65 deletions.
4 changes: 4 additions & 0 deletions phpcs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
<exclude-pattern>src/Fluent/QueryBuilder.php</exclude-pattern>
</rule>

<rule ref="SlevomatCodingStandard.Classes.ClassConstantVisibility.MissingConstantVisibility">
<exclude-pattern>src/Fluent/Sql.php</exclude-pattern>
</rule>

<rule ref="SlevomatCodingStandard.Classes.ModernClassNameReference.ClassNameReferencedViaFunctionCall">
<exclude-pattern>src/Fluent/Exceptions/ComplexException.php</exclude-pattern>
<exclude-pattern>tests/Integration/DocsTest.php</exclude-pattern>
Expand Down
2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ parameters:
# === PHPStan imperfection ===

-
message: "#^Property Forrest79\\\\PhPgSql\\\\Fluent\\\\Query::\\$params \\(array\\{select: array\\<int\\|string, BackedEnum\\|Forrest79\\\\PhPgSql\\\\Db\\\\Sql\\|Forrest79\\\\PhPgSql\\\\Fluent\\\\Query\\|int\\|string\\>, distinct: bool, tables: array\\<string, array\\{string, string\\}\\>, table\\-types: array\\{main: string\\|null, from: list\\<string\\>, joins: list\\<string\\>\\}, join\\-conditions: array\\<string, Forrest79\\\\PhPgSql\\\\Fluent\\\\Complex\\>, lateral\\-tables: array\\<string, string\\>, where: Forrest79\\\\PhPgSql\\\\Fluent\\\\Complex\\|null, groupBy: array\\<string\\>, \\.\\.\\.\\}\\) does not accept .+\\.$#"
message: "#^Property Forrest79\\\\PhPgSql\\\\Fluent\\\\Query::\\$params \\(array\\{select: array\\<int\\|string, BackedEnum\\|Forrest79\\\\PhPgSql\\\\Db\\\\Sql\\|Forrest79\\\\PhPgSql\\\\Fluent\\\\Query\\|int\\|string\\>, distinct: bool, tables: array\\<string, array\\{string, string\\}\\>, table\\-types: array\\{main: string\\|null, from: list\\<string\\>, joins: list\\<string\\>, using: string\\|null\\}, on\\-conditions: array\\<string, Forrest79\\\\PhPgSql\\\\Fluent\\\\Complex\\>, lateral\\-tables: array\\<string, string\\>, where: Forrest79\\\\PhPgSql\\\\Fluent\\\\Complex\\|null, groupBy: array\\<string\\>, \\.\\.\\.\\}\\) does not accept .+\\.$#"
path: src/Fluent/Query.php
count: 5

Expand Down
34 changes: 19 additions & 15 deletions src/Fluent/Exceptions/QueryBuilderException.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ class QueryBuilderException extends Exception
{
public const BAD_QUERY_TYPE = 1;
public const NO_COLUMNS_TO_SELECT = 2;
public const NO_JOIN_CONDITIONS = 3;
public const NO_ON_CONDITION = 3;
public const NO_DATA_TO_INSERT = 4;
public const NO_DATA_TO_UPDATE = 5;
public const DATA_CANT_CONTAIN_ARRAY = 6;
public const NO_MAIN_TABLE = 7;
public const BAD_PARAMS_COUNT = 8;
public const BAD_PARAM = 9;
public const NO_CORRESPONDING_TABLE = 10;
public const SELECT_ALL_COLUMNS_CANT_BE_COMBINED_WITH_CONCRETE_COLUMN_FOR_INSERT_SELECT_WITH_COLUMN_DETECTION = 11;
public const NO_CORRESPONDING_TABLE = 9;
public const SELECT_ALL_COLUMNS_CANT_BE_COMBINED_WITH_CONCRETE_COLUMN_FOR_INSERT_SELECT_WITH_COLUMN_DETECTION = 10;
public const NO_USING = 11;
public const NO_WHEN = 12;


public static function badQueryType(string $type): self
Expand All @@ -29,9 +30,9 @@ public static function noColumnsToSelect(): self
}


public static function noJoinConditions(string $alias): self
public static function noOnCondition(string $alias): self
{
return new self(\sprintf('No join conditions for table alias \'%s\'.', $alias), self::NO_JOIN_CONDITIONS);
return new self(\sprintf('There is no conditions for ON for table alias \'%s\'.', $alias), self::NO_ON_CONDITION);
}


Expand Down Expand Up @@ -65,15 +66,6 @@ public static function badParamsCount(string $condition, int $expected, int $act
}


/**
* @param array<string> $validValues
*/
public static function badParam(string $param, string $value, array $validValues): self
{
return new self(\sprintf('Bad param \'%s\' with value \'%s\'. Valid values are \'%s\'.', $param, $value, \implode('\', \'', $validValues)), self::BAD_PARAM);
}


/**
* @param array<string> $aliases
*/
Expand All @@ -88,4 +80,16 @@ public static function selectAllColumnsCantBeCombinedWithConcreteColumnForInsert
return new self('You can\'t use \'SELECT *\' and also some concrete column for INSERT - SELECT with column detection.', self::SELECT_ALL_COLUMNS_CANT_BE_COMBINED_WITH_CONCRETE_COLUMN_FOR_INSERT_SELECT_WITH_COLUMN_DETECTION);
}


public static function noUsing(): self
{
return new self('No USING for MERGE.', self::NO_USING);
}


public static function noWhen(): self
{
return new self('No WHEN for MERGE.', self::NO_WHEN);
}

}
7 changes: 7 additions & 0 deletions src/Fluent/Exceptions/QueryException.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class QueryException extends Exception
public const PARAM_MUST_BE_SCALAR_OR_ENUM_OR_EXPRESSION = 5;
public const CANT_UPDATE_QUERY_AFTER_EXECUTE = 6;
public const YOU_MUST_EXECUTE_QUERY_BEFORE_THAT = 7;
public const ONLY_ONE_USING = 8;


public static function onlyOneMainTable(): self
Expand Down Expand Up @@ -57,4 +58,10 @@ public static function youMustExecuteQueryBeforeThat(): self
return new self('You must execute query before that', self::YOU_MUST_EXECUTE_QUERY_BEFORE_THAT);
}


public static function onlyOneUsing(): self
{
return new self('USING can be set only once.', self::ONLY_ONE_USING);
}

}
57 changes: 30 additions & 27 deletions src/Fluent/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class Query implements Sql
public const PARAM_DISTINCT = 'distinct';
public const PARAM_TABLES = 'tables';
public const PARAM_TABLE_TYPES = 'table-types';
public const PARAM_JOIN_CONDITIONS = 'join-conditions';
public const PARAM_ON_CONDITIONS = 'on-conditions';
public const PARAM_LATERAL_TABLES = 'lateral-tables';
public const PARAM_WHERE = 'where';
public const PARAM_GROUPBY = 'groupBy';
Expand All @@ -41,6 +41,7 @@ class Query implements Sql
public const TABLE_TYPE_MAIN = 'main';
public const TABLE_TYPE_FROM = 'from';
public const TABLE_TYPE_JOINS = 'joins';
public const TABLE_TYPE_USING = 'using';

private const JOIN_INNER = 'INNER JOIN';
private const JOIN_LEFT_OUTER = 'LEFT OUTER JOIN';
Expand All @@ -53,9 +54,6 @@ class Query implements Sql
private const COMBINE_INTERSECT = 'INTERSECT';
private const COMBINE_EXCEPT = 'EXCEPT';

public const MERGE_USING_DATA_SOURCE = 'using-data-source';
public const MERGE_USING_ALIAS = 'using-alias';
public const MERGE_WHEN = 'when';
public const MERGE_WHEN_MATCHED = 'when-matched';
public const MERGE_WHEN_NOT_MATCHED = 'when-not-matched';

Expand All @@ -72,8 +70,9 @@ class Query implements Sql
self::TABLE_TYPE_MAIN => NULL,
self::TABLE_TYPE_FROM => [],
self::TABLE_TYPE_JOINS => [],
self::TABLE_TYPE_USING => NULL,
],
self::PARAM_JOIN_CONDITIONS => [],
self::PARAM_ON_CONDITIONS => [],
self::PARAM_LATERAL_TABLES => [],
self::PARAM_WHERE => NULL,
self::PARAM_GROUPBY => [],
Expand All @@ -86,11 +85,7 @@ class Query implements Sql
self::PARAM_RETURNING => [],
self::PARAM_DATA => [],
self::PARAM_ROWS => [],
self::PARAM_MERGE => [
self::MERGE_USING_DATA_SOURCE => NULL,
self::MERGE_USING_ALIAS => [],
self::MERGE_WHEN => [],
],
self::PARAM_MERGE => [],
self::PARAM_WITH => [
self::WITH_QUERIES => [],
self::WITH_QUERIES_SUFFIX => [],
Expand Down Expand Up @@ -302,8 +297,8 @@ private function addTable(string $type, $name, ?string $alias, $onCondition = NU

$this->checkAlias($name, $alias);

if (($type === self::TABLE_TYPE_MAIN) && ($this->params[self::PARAM_TABLE_TYPES][self::TABLE_TYPE_MAIN] !== NULL)) {
throw Exceptions\QueryException::onlyOneMainTable();
if (in_array($type, [self::TABLE_TYPE_MAIN, self::TABLE_TYPE_USING], TRUE) && ($this->params[self::PARAM_TABLE_TYPES][$type] !== NULL)) {
throw ($type === self::TABLE_TYPE_MAIN) ? Exceptions\QueryException::onlyOneMainTable() : Exceptions\QueryException::onlyOneUsing();
}

if ($alias === NULL) {
Expand All @@ -317,14 +312,14 @@ private function addTable(string $type, $name, ?string $alias, $onCondition = NU

$this->params[self::PARAM_TABLES][$alias] = [$name, $type];

if ($type === self::TABLE_TYPE_MAIN) {
if (in_array($type, [self::TABLE_TYPE_MAIN, self::TABLE_TYPE_USING], TRUE)) {
$this->params[self::PARAM_TABLE_TYPES][$type] = $alias;
} else {
$this->params[self::PARAM_TABLE_TYPES][$type === self::TABLE_TYPE_FROM ? $type : self::TABLE_TYPE_JOINS][] = $alias;
}

if ($onCondition !== NULL) {
$this->getComplexParam(self::PARAM_JOIN_CONDITIONS, $alias)->add($onCondition);
$this->getComplexParam(self::PARAM_ON_CONDITIONS, $alias)->add($onCondition);
}

return $this;
Expand All @@ -340,7 +335,7 @@ private function addTable(string $type, $name, ?string $alias, $onCondition = NU
public function on(string $alias, $condition, ...$params): self
{
$this->resetQuery();
$this->getComplexParam(self::PARAM_JOIN_CONDITIONS, $alias)->add($condition, ...$params);
$this->getComplexParam(self::PARAM_ON_CONDITIONS, $alias)->add($condition, ...$params);
return $this;
}

Expand Down Expand Up @@ -450,12 +445,12 @@ public function havingOr(array $conditions = []): Complex

private function getComplexParam(string $param, ?string $alias = NULL): Complex
{
if ($param === self::PARAM_JOIN_CONDITIONS) {
if ($param === self::PARAM_ON_CONDITIONS) {
if (!isset($this->params[$param][$alias])) {
$this->params[$param][$alias] = Complex::createAnd();
}
return $this->params[$param][$alias];
} elseif (($param === self::PARAM_WHERE) || ($param === self::PARAM_HAVING)) {
} else if (($param === self::PARAM_WHERE) || ($param === self::PARAM_HAVING)) {
if ($this->params[$param] === NULL) {
$this->params[$param] = Complex::createAnd();
}
Expand Down Expand Up @@ -663,7 +658,7 @@ public function merge(?string $into = NULL, ?string $alias = NULL): self
{
$this->resetQuery();

$this->queryType = self::QUERY_DELETE;
$this->queryType = self::QUERY_MERGE;

if ($into !== NULL) {
$this->table($into, $alias);
Expand All @@ -681,11 +676,7 @@ public function merge(?string $into = NULL, ?string $alias = NULL): self
*/
public function using($dataSource, ?string $alias = NULL, $onCondition = NULL): self
{
$this->resetQuery();
$this->queryType = self::QUERY_MERGE;
$this->params[self::MERGE_USING_DATA_SOURCE] = $dataSource;
$this->params[self::MERGE_USING_ALIAS] = $alias;
return $this;
return $this->addTable(self::TABLE_TYPE_USING, $dataSource, $alias, $onCondition);
}


Expand All @@ -697,7 +688,13 @@ public function using($dataSource, ?string $alias = NULL, $onCondition = NULL):
public function whenMatched(string $then, $onCondition = NULL): self
{
$this->resetQuery();
$this->params[self::MERGE_WHEN][] = [self::MERGE_WHEN_MATCHED, $then, $onCondition];

$this->params[self::PARAM_MERGE][] = [
self::MERGE_WHEN_MATCHED,
$then,
$onCondition === NULL ? NULL : Complex::createAnd()->add($onCondition),
];

return $this;
}

Expand All @@ -710,7 +707,13 @@ public function whenMatched(string $then, $onCondition = NULL): self
public function whenNotMatched(string $then, $onCondition = NULL): self
{
$this->resetQuery();
$this->params[self::MERGE_WHEN][] = [self::MERGE_WHEN_NOT_MATCHED, $then, $onCondition];

$this->params[self::PARAM_MERGE][] = [
self::MERGE_WHEN_NOT_MATCHED,
$then,
$onCondition === NULL ? NULL : Complex::createAnd()->add($onCondition),
];

return $this;
}

Expand Down Expand Up @@ -888,8 +891,8 @@ public function __clone()
{
$this->resetQuery();

foreach ($this->params[self::PARAM_JOIN_CONDITIONS] as $alias => $joinCondition) {
$this->params[self::PARAM_JOIN_CONDITIONS][$alias] = clone $joinCondition;
foreach ($this->params[self::PARAM_ON_CONDITIONS] as $alias => $joinCondition) {
$this->params[self::PARAM_ON_CONDITIONS][$alias] = clone $joinCondition;
}

if ($this->params[self::PARAM_WHERE] !== NULL) {
Expand Down
Loading

0 comments on commit 2e0b93c

Please sign in to comment.