Skip to content
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ composer.phar
coverage
coverage.xml
.DS_Store
.claude/worktrees/
.claude/
.phpunit.cache/
coverage/
9 changes: 0 additions & 9 deletions src/Query/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -390,15 +390,6 @@ private function compileWhenCondition(WhenClause $when): string
}
}

/**
* Build an INSERT ... ON CONFLICT/DUPLICATE KEY UPDATE statement.
* Requires onConflict() to be called first to configure conflict keys and update columns.
*/
public function upsert(): Statement
{
throw new UnsupportedException('UPSERT is not supported by this dialect.');
}

#[\Override]
public function build(): Statement
{
Expand Down
10 changes: 10 additions & 0 deletions src/Query/Builder/Feature/InsertOrIgnore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Utopia\Query\Builder\Feature;

use Utopia\Query\Builder\Statement;

interface InsertOrIgnore
{
public function insertOrIgnore(): Statement;
}
4 changes: 0 additions & 4 deletions src/Query/Builder/Feature/Upsert.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,4 @@
interface Upsert
{
public function upsert(): Statement;

public function insertOrIgnore(): Statement;

public function upsertSelect(): Statement;
}
10 changes: 10 additions & 0 deletions src/Query/Builder/Feature/UpsertSelect.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Utopia\Query\Builder\Feature;

use Utopia\Query\Builder\Statement;

interface UpsertSelect
{
public function upsertSelect(): Statement;
}
9 changes: 2 additions & 7 deletions src/Query/Builder/MongoDB.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use stdClass;
use Utopia\Query\Builder as BaseBuilder;
use Utopia\Query\Builder\Feature\FullTextSearch;
use Utopia\Query\Builder\Feature\InsertOrIgnore;
use Utopia\Query\Builder\Feature\MongoDB\ArrayPushModifiers;
use Utopia\Query\Builder\Feature\MongoDB\AtlasSearch;
use Utopia\Query\Builder\Feature\MongoDB\ConditionalArrayUpdates;
Expand All @@ -22,6 +23,7 @@

class MongoDB extends BaseBuilder implements
Upsert,
InsertOrIgnore,
FullTextSearch,
TableSampling,
FieldUpdates,
Expand Down Expand Up @@ -358,7 +360,6 @@ public function delete(): Statement
);
}

#[\Override]
public function upsert(): Statement
{
$this->bindings = [];
Expand Down Expand Up @@ -436,12 +437,6 @@ public function insertOrIgnore(): Statement
);
}

#[\Override]
public function upsertSelect(): Statement
{
throw new UnsupportedException('upsertSelect() is not supported in MongoDB builder.');
}

private function needsAggregation(ParsedQuery $grouped): bool
{
if (! empty(Query::getByType($this->pendingQueries, [Method::OrderRandom], false))) {
Expand Down
22 changes: 21 additions & 1 deletion src/Query/Builder/MySQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,41 @@
namespace Utopia\Query\Builder;

use Utopia\Query\Builder\Feature\ConditionalAggregates;
use Utopia\Query\Builder\Feature\FullTextSearch;
use Utopia\Query\Builder\Feature\GroupByModifiers;
use Utopia\Query\Builder\Feature\Hints;
use Utopia\Query\Builder\Feature\InsertOrIgnore;
use Utopia\Query\Builder\Feature\Json;
use Utopia\Query\Builder\Feature\LateralJoins;
use Utopia\Query\Builder\Feature\Spatial;
use Utopia\Query\Builder\Feature\StringAggregates;
use Utopia\Query\Builder\Feature\Upsert;
use Utopia\Query\Builder\Feature\UpsertSelect;
use Utopia\Query\Exception\ValidationException;
use Utopia\Query\Method;

class MySQL extends SQL implements Json, Hints, ConditionalAggregates, LateralJoins, StringAggregates, GroupByModifiers
class MySQL extends SQL implements
Json,
Hints,
ConditionalAggregates,
LateralJoins,
StringAggregates,
GroupByModifiers,
Spatial,
FullTextSearch,
Upsert,
UpsertSelect,
InsertOrIgnore
{
use Trait\ConditionalAggregates;
use Trait\FullTextSearch;
use Trait\GroupByModifiers;
use Trait\Hints;
use Trait\LateralJoins;
use Trait\Spatial;
use Trait\StringAggregates;
use Trait\Upsert;
use Trait\UpsertSelect;

protected string $updateJoinTable = '';

Expand Down
45 changes: 36 additions & 9 deletions src/Query/Builder/PostgreSQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
use Utopia\Query\AST\Serializer\PostgreSQL as PostgreSQLSerializer;
use Utopia\Query\Builder\Feature\ConditionalAggregates;
use Utopia\Query\Builder\Feature\FullOuterJoins;
use Utopia\Query\Builder\Feature\FullTextSearch;
use Utopia\Query\Builder\Feature\GroupByModifiers;
use Utopia\Query\Builder\Feature\InsertOrIgnore;
use Utopia\Query\Builder\Feature\Json;
use Utopia\Query\Builder\Feature\LateralJoins;
use Utopia\Query\Builder\Feature\PostgreSQL\AggregateFilter;
Expand All @@ -17,8 +19,11 @@
use Utopia\Query\Builder\Feature\PostgreSQL\Returning;
use Utopia\Query\Builder\Feature\PostgreSQL\VectorSearch;
use Utopia\Query\Builder\Feature\Sequences;
use Utopia\Query\Builder\Feature\Spatial;
use Utopia\Query\Builder\Feature\StringAggregates;
use Utopia\Query\Builder\Feature\TableSampling;
use Utopia\Query\Builder\Feature\Upsert;
use Utopia\Query\Builder\Feature\UpsertSelect;
use Utopia\Query\Builder\PostgreSQL\DeleteUsing;
use Utopia\Query\Builder\PostgreSQL\MergeTarget;
use Utopia\Query\Builder\PostgreSQL\UpdateFrom;
Expand All @@ -27,9 +32,30 @@
use Utopia\Query\Query;
use Utopia\Query\Schema\ColumnType;

class PostgreSQL extends SQL implements VectorSearch, Json, Returning, LockingOf, ConditionalAggregates, Merge, LateralJoins, TableSampling, FullOuterJoins, StringAggregates, OrderedSetAggregates, DistinctOn, AggregateFilter, GroupByModifiers, Sequences
class PostgreSQL extends SQL implements
VectorSearch,
Json,
Returning,
LockingOf,
ConditionalAggregates,
Merge,
LateralJoins,
TableSampling,
FullOuterJoins,
StringAggregates,
OrderedSetAggregates,
DistinctOn,
AggregateFilter,
GroupByModifiers,
Sequences,
Spatial,
FullTextSearch,
Upsert,
UpsertSelect,
InsertOrIgnore
{
use Trait\FullOuterJoins;
use Trait\FullTextSearch;
use Trait\GroupByModifiers;
use Trait\LateralJoins;
use Trait\PostgreSQL\AggregateFilter;
Expand All @@ -40,7 +66,14 @@ class PostgreSQL extends SQL implements VectorSearch, Json, Returning, LockingOf
use Trait\PostgreSQL\Sequences;
use Trait\PostgreSQL\VectorSearch;
use Trait\Returning;
use Trait\Spatial;
use Trait\StringAggregates;
use Trait\Upsert {
upsert as private baseUpsert;
}
use Trait\UpsertSelect {
upsertSelect as private baseUpsertSelect;
}

protected string $wrapChar = '"';

Expand Down Expand Up @@ -337,20 +370,14 @@ private function mergeIntoWhereClause(array &$parts, array $extra): void
$parts[] = 'WHERE ' . \implode(' AND ', $extra);
}

#[\Override]
public function upsert(): Statement
{
$result = parent::upsert();

return $this->appendReturning($result);
return $this->appendReturning($this->baseUpsert());
}

#[\Override]
public function upsertSelect(): Statement
{
$result = parent::upsertSelect();

return $this->appendReturning($result);
return $this->appendReturning($this->baseUpsertSelect());
}

#[\Override]
Expand Down
10 changes: 1 addition & 9 deletions src/Query/Builder/SQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,26 @@

use Utopia\Query\Builder as BaseBuilder;
use Utopia\Query\Builder\Feature\BitwiseAggregates;
use Utopia\Query\Builder\Feature\FullTextSearch;
use Utopia\Query\Builder\Feature\Locking;
use Utopia\Query\Builder\Feature\Spatial;
use Utopia\Query\Builder\Feature\StatisticalAggregates;
use Utopia\Query\Builder\Feature\Transactions;
use Utopia\Query\Builder\Feature\Upsert;
use Utopia\Query\Method;
use Utopia\Query\Query;
use Utopia\Query\QuotesIdentifiers;
use Utopia\Query\Schema\ColumnType;

abstract class SQL extends BaseBuilder implements Locking, Transactions, Upsert, Spatial, FullTextSearch, StatisticalAggregates, BitwiseAggregates
abstract class SQL extends BaseBuilder implements Locking, Transactions, StatisticalAggregates, BitwiseAggregates
{
use QuotesIdentifiers;
use Trait\BitwiseAggregates;
use Trait\FullTextSearch;
use Trait\Json;
use Trait\Locking;
use Trait\Spatial;
use Trait\StatisticalAggregates;
use Trait\Transactions;
use Trait\Upsert;

/** @var array<string, Condition> */
protected array $jsonSets = [];

abstract public function insertOrIgnore(): Statement;

abstract protected function compileConflictHeader(): string;

abstract protected function compileConflictAssignment(string $wrapped): string;
Expand Down
7 changes: 6 additions & 1 deletion src/Query/Builder/SQLite.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@
use Utopia\Query\AST\Serializer;
use Utopia\Query\AST\Serializer\SQLite as SQLiteSerializer;
use Utopia\Query\Builder\Feature\ConditionalAggregates;
use Utopia\Query\Builder\Feature\InsertOrIgnore;
use Utopia\Query\Builder\Feature\Json;
use Utopia\Query\Builder\Feature\StringAggregates;
use Utopia\Query\Builder\Feature\Upsert;
use Utopia\Query\Builder\Feature\UpsertSelect;
use Utopia\Query\Exception\UnsupportedException;
use Utopia\Query\Exception\ValidationException;
use Utopia\Query\Method;

class SQLite extends SQL implements Json, ConditionalAggregates, StringAggregates
class SQLite extends SQL implements Json, ConditionalAggregates, StringAggregates, InsertOrIgnore, Upsert, UpsertSelect
{
use Trait\ConditionalAggregates;
use Trait\StringAggregates;
use Trait\Upsert;
use Trait\UpsertSelect;

/** @var array<string, Condition> */
protected array $jsonSets = [];
Expand Down
38 changes: 0 additions & 38 deletions src/Query/Builder/Trait/Upsert.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

trait Upsert
{
#[\Override]
public function upsert(): Statement
{
$this->bindings = [];
Expand Down Expand Up @@ -62,41 +61,4 @@ public function upsert(): Statement

return new Statement($sql, $this->bindings, executor: $this->executor);
}

#[\Override]
public function upsertSelect(): Statement
{
$this->bindings = [];
$this->validateTable();

if ($this->insertSelectSource === null) {
throw new ValidationException('No SELECT source specified. Call fromSelect() before upsertSelect().');
}
if (empty($this->insertSelectColumns)) {
throw new ValidationException('No columns specified. Call fromSelect() with columns before upsertSelect().');
}
if (empty($this->conflictKeys)) {
throw new ValidationException('No conflict keys specified. Call onConflict() before upsertSelect().');
}
if (empty($this->conflictUpdateColumns)) {
throw new ValidationException('No conflict update columns specified. Call onConflict() with update columns before upsertSelect().');
}

$wrappedColumns = \array_map(
fn (string $col): string => $this->resolveAndWrap($col),
$this->insertSelectColumns
);

$sourceResult = $this->insertSelectSource->build();

$sql = 'INSERT INTO ' . $this->quote($this->table)
. ' (' . \implode(', ', $wrappedColumns) . ')'
. ' ' . $sourceResult->query;

$this->addBindings($sourceResult->bindings);

$sql .= ' ' . $this->compileConflictClause();

return new Statement($sql, $this->bindings, executor: $this->executor);
}
}
45 changes: 45 additions & 0 deletions src/Query/Builder/Trait/UpsertSelect.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace Utopia\Query\Builder\Trait;

use Utopia\Query\Builder\Statement;
use Utopia\Query\Exception\ValidationException;

trait UpsertSelect
{
public function upsertSelect(): Statement
{
$this->bindings = [];
$this->validateTable();

if ($this->insertSelectSource === null) {
throw new ValidationException('No SELECT source specified. Call fromSelect() before upsertSelect().');
}
if (empty($this->insertSelectColumns)) {
throw new ValidationException('No columns specified. Call fromSelect() with columns before upsertSelect().');
}
if (empty($this->conflictKeys)) {
throw new ValidationException('No conflict keys specified. Call onConflict() before upsertSelect().');
}
if (empty($this->conflictUpdateColumns)) {
throw new ValidationException('No conflict update columns specified. Call onConflict() with update columns before upsertSelect().');
}

$wrappedColumns = \array_map(
fn (string $col): string => $this->resolveAndWrap($col),
$this->insertSelectColumns
);

$sourceResult = $this->insertSelectSource->build();

$sql = 'INSERT INTO ' . $this->quote($this->table)
. ' (' . \implode(', ', $wrappedColumns) . ')'
. ' ' . $sourceResult->query;

$this->addBindings($sourceResult->bindings);

$sql .= ' ' . $this->compileConflictClause();

return new Statement($sql, $this->bindings, executor: $this->executor);
}
}
Loading
Loading