Skip to content

Commit a502794

Browse files
committed
Merge branch 'main' of github.com:utopia-php/database into get-sequence-default
2 parents 79ed2c9 + 61dd323 commit a502794

File tree

12 files changed

+1022
-37
lines changed

12 files changed

+1022
-37
lines changed

src/Database/Adapter/MariaDB.php

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,39 +1696,69 @@ protected function getSQLCondition(Query $query, array &$binds): string
16961696

16971697
return "MATCH({$alias}.{$attribute}) AGAINST (:{$placeholder}_0 IN BOOLEAN MODE)";
16981698

1699+
case Query::TYPE_NOT_SEARCH:
1700+
$binds[":{$placeholder}_0"] = $this->getFulltextValue($query->getValue());
1701+
1702+
return "NOT (MATCH({$alias}.{$attribute}) AGAINST (:{$placeholder}_0 IN BOOLEAN MODE))";
1703+
16991704
case Query::TYPE_BETWEEN:
17001705
$binds[":{$placeholder}_0"] = $query->getValues()[0];
17011706
$binds[":{$placeholder}_1"] = $query->getValues()[1];
17021707

17031708
return "{$alias}.{$attribute} BETWEEN :{$placeholder}_0 AND :{$placeholder}_1";
17041709

1710+
case Query::TYPE_NOT_BETWEEN:
1711+
$binds[":{$placeholder}_0"] = $query->getValues()[0];
1712+
$binds[":{$placeholder}_1"] = $query->getValues()[1];
1713+
1714+
return "{$alias}.{$attribute} NOT BETWEEN :{$placeholder}_0 AND :{$placeholder}_1";
1715+
17051716
case Query::TYPE_IS_NULL:
17061717
case Query::TYPE_IS_NOT_NULL:
17071718

17081719
return "{$alias}.{$attribute} {$this->getSQLOperator($query->getMethod())}";
17091720

17101721
case Query::TYPE_CONTAINS:
1722+
case Query::TYPE_NOT_CONTAINS:
17111723
if ($this->getSupportForJSONOverlaps() && $query->onArray()) {
17121724
$binds[":{$placeholder}_0"] = json_encode($query->getValues());
1713-
return "JSON_OVERLAPS({$alias}.{$attribute}, :{$placeholder}_0)";
1725+
$isNot = $query->getMethod() === Query::TYPE_NOT_CONTAINS;
1726+
return $isNot
1727+
? "NOT (JSON_OVERLAPS({$alias}.{$attribute}, :{$placeholder}_0))"
1728+
: "JSON_OVERLAPS({$alias}.{$attribute}, :{$placeholder}_0)";
17141729
}
17151730

17161731
// no break! continue to default case
17171732
default:
17181733
$conditions = [];
1734+
$isNotQuery = in_array($query->getMethod(), [
1735+
Query::TYPE_NOT_STARTS_WITH,
1736+
Query::TYPE_NOT_ENDS_WITH,
1737+
Query::TYPE_NOT_CONTAINS
1738+
]);
1739+
17191740
foreach ($query->getValues() as $key => $value) {
17201741
$value = match ($query->getMethod()) {
17211742
Query::TYPE_STARTS_WITH => $this->escapeWildcards($value) . '%',
1743+
Query::TYPE_NOT_STARTS_WITH => $this->escapeWildcards($value) . '%',
17221744
Query::TYPE_ENDS_WITH => '%' . $this->escapeWildcards($value),
1745+
Query::TYPE_NOT_ENDS_WITH => '%' . $this->escapeWildcards($value),
17231746
Query::TYPE_CONTAINS => $query->onArray() ? \json_encode($value) : '%' . $this->escapeWildcards($value) . '%',
1747+
Query::TYPE_NOT_CONTAINS => $query->onArray() ? \json_encode($value) : '%' . $this->escapeWildcards($value) . '%',
17241748
default => $value
17251749
};
17261750

17271751
$binds[":{$placeholder}_{$key}"] = $value;
1728-
$conditions[] = "{$alias}.{$attribute} {$this->getSQLOperator($query->getMethod())} :{$placeholder}_{$key}";
1752+
1753+
if ($isNotQuery) {
1754+
$conditions[] = "{$alias}.{$attribute} NOT {$this->getSQLOperator($query->getMethod())} :{$placeholder}_{$key}";
1755+
} else {
1756+
$conditions[] = "{$alias}.{$attribute} {$this->getSQLOperator($query->getMethod())} :{$placeholder}_{$key}";
1757+
}
17291758
}
17301759

1731-
return empty($conditions) ? '' : '(' . implode(' OR ', $conditions) . ')';
1760+
$separator = $isNotQuery ? ' AND ' : ' OR ';
1761+
return empty($conditions) ? '' : '(' . implode($separator, $conditions) . ')';
17321762
}
17331763
}
17341764

@@ -1971,9 +2001,6 @@ public function getSupportForNumericCasting(): bool
19712001

19722002
public function getSupportForIndexArray(): bool
19732003
{
1974-
/**
1975-
* Disabled to be compatible with Mysql adapter
1976-
*/
1977-
return false;
2004+
return true;
19782005
}
19792006
}

src/Database/Adapter/MySQL.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,9 @@ public function getSizeOfCollectionOnDisk(string $collection): int
8181
public function getSupportForIndexArray(): bool
8282
{
8383
/**
84-
* Disabling index creation due to Mysql bug
8584
* @link https://bugs.mysql.com/bug.php?id=111037
8685
*/
87-
return false;
86+
return true;
8887
}
8988

9089
public function getSupportForCastIndexArray(): bool

src/Database/Adapter/Postgres.php

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1794,36 +1794,67 @@ protected function getSQLCondition(Query $query, array &$binds): string
17941794
$binds[":{$placeholder}_0"] = $this->getFulltextValue($query->getValue());
17951795
return "to_tsvector(regexp_replace({$attribute}, '[^\w]+',' ','g')) @@ websearch_to_tsquery(:{$placeholder}_0)";
17961796

1797+
case Query::TYPE_NOT_SEARCH:
1798+
$binds[":{$placeholder}_0"] = $this->getFulltextValue($query->getValue());
1799+
return "NOT (to_tsvector(regexp_replace({$attribute}, '[^\w]+',' ','g')) @@ websearch_to_tsquery(:{$placeholder}_0))";
1800+
17971801
case Query::TYPE_BETWEEN:
17981802
$binds[":{$placeholder}_0"] = $query->getValues()[0];
17991803
$binds[":{$placeholder}_1"] = $query->getValues()[1];
18001804
return "{$alias}.{$attribute} BETWEEN :{$placeholder}_0 AND :{$placeholder}_1";
18011805

1806+
case Query::TYPE_NOT_BETWEEN:
1807+
$binds[":{$placeholder}_0"] = $query->getValues()[0];
1808+
$binds[":{$placeholder}_1"] = $query->getValues()[1];
1809+
return "{$alias}.{$attribute} NOT BETWEEN :{$placeholder}_0 AND :{$placeholder}_1";
1810+
18021811
case Query::TYPE_IS_NULL:
18031812
case Query::TYPE_IS_NOT_NULL:
18041813
return "{$alias}.{$attribute} {$this->getSQLOperator($query->getMethod())}";
18051814

18061815
case Query::TYPE_CONTAINS:
1807-
$operator = $query->onArray() ? '@>' : null;
1816+
case Query::TYPE_NOT_CONTAINS:
1817+
if ($query->onArray()) {
1818+
$operator = '@>';
1819+
} else {
1820+
$operator = null;
1821+
}
18081822

18091823
// no break
18101824
default:
18111825
$conditions = [];
18121826
$operator = $operator ?? $this->getSQLOperator($query->getMethod());
1827+
$isNotQuery = in_array($query->getMethod(), [
1828+
Query::TYPE_NOT_STARTS_WITH,
1829+
Query::TYPE_NOT_ENDS_WITH,
1830+
Query::TYPE_NOT_CONTAINS
1831+
]);
18131832

18141833
foreach ($query->getValues() as $key => $value) {
18151834
$value = match ($query->getMethod()) {
18161835
Query::TYPE_STARTS_WITH => $this->escapeWildcards($value) . '%',
1836+
Query::TYPE_NOT_STARTS_WITH => $this->escapeWildcards($value) . '%',
18171837
Query::TYPE_ENDS_WITH => '%' . $this->escapeWildcards($value),
1838+
Query::TYPE_NOT_ENDS_WITH => '%' . $this->escapeWildcards($value),
18181839
Query::TYPE_CONTAINS => $query->onArray() ? \json_encode($value) : '%' . $this->escapeWildcards($value) . '%',
1840+
Query::TYPE_NOT_CONTAINS => $query->onArray() ? \json_encode($value) : '%' . $this->escapeWildcards($value) . '%',
18191841
default => $value
18201842
};
18211843

18221844
$binds[":{$placeholder}_{$key}"] = $value;
1823-
$conditions[] = "{$alias}.{$attribute} {$operator} :{$placeholder}_{$key}";
1845+
1846+
if ($isNotQuery && $query->onArray()) {
1847+
// For array NOT queries, wrap the entire condition in NOT()
1848+
$conditions[] = "NOT ({$alias}.{$attribute} {$operator} :{$placeholder}_{$key})";
1849+
} elseif ($isNotQuery && !$query->onArray()) {
1850+
$conditions[] = "{$alias}.{$attribute} NOT {$operator} :{$placeholder}_{$key}";
1851+
} else {
1852+
$conditions[] = "{$alias}.{$attribute} {$operator} :{$placeholder}_{$key}";
1853+
}
18241854
}
18251855

1826-
return empty($conditions) ? '' : '(' . implode(' OR ', $conditions) . ')';
1856+
$separator = $isNotQuery ? ' AND ' : ' OR ';
1857+
return empty($conditions) ? '' : '(' . implode($separator, $conditions) . ')';
18271858
}
18281859
}
18291860

src/Database/Adapter/SQL.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,6 +1501,9 @@ protected function getSQLOperator(string $method): string
15011501
case Query::TYPE_STARTS_WITH:
15021502
case Query::TYPE_ENDS_WITH:
15031503
case Query::TYPE_CONTAINS:
1504+
case Query::TYPE_NOT_STARTS_WITH:
1505+
case Query::TYPE_NOT_ENDS_WITH:
1506+
case Query::TYPE_NOT_CONTAINS:
15041507
return $this->getLikeOperator();
15051508
default:
15061509
throw new DatabaseException('Unknown method: ' . $method);

src/Database/Database.php

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,11 @@ class Database
348348

349349
protected bool $filter = true;
350350

351+
/**
352+
* @var array<string, bool>|null
353+
*/
354+
protected ?array $disabledFilters = [];
355+
351356
protected bool $validate = true;
352357

353358
protected bool $preserveDates = false;
@@ -813,17 +818,35 @@ public function disableFilters(): static
813818
*
814819
* @template T
815820
* @param callable(): T $callback
821+
* @param array<string>|null $filters
816822
* @return T
817823
*/
818-
public function skipFilters(callable $callback): mixed
824+
public function skipFilters(callable $callback, ?array $filters = null): mixed
819825
{
820-
$initial = $this->filter;
821-
$this->disableFilters();
826+
if (empty($filters)) {
827+
$initial = $this->filter;
828+
$this->disableFilters();
829+
830+
try {
831+
return $callback();
832+
} finally {
833+
$this->filter = $initial;
834+
}
835+
}
836+
837+
$previous = $this->filter;
838+
$previousDisabled = $this->disabledFilters;
839+
$disabled = [];
840+
foreach ($filters as $name) {
841+
$disabled[$name] = true;
842+
}
843+
$this->disabledFilters = $disabled;
822844

823845
try {
824846
return $callback();
825847
} finally {
826-
$this->filter = $initial;
848+
$this->filter = $previous;
849+
$this->disabledFilters = $previousDisabled;
827850
}
828851
}
829852

@@ -3324,9 +3347,15 @@ private function populateDocumentRelationships(Document $collection, Document $d
33243347
{
33253348
$attributes = $collection->getAttribute('attributes', []);
33263349

3327-
$relationships = \array_filter($attributes, function ($attribute) {
3328-
return $attribute['type'] === Database::VAR_RELATIONSHIP;
3329-
});
3350+
$relationships = [];
3351+
3352+
foreach ($attributes as $attribute) {
3353+
if ($attribute['type'] === Database::VAR_RELATIONSHIP) {
3354+
if (empty($selects) || array_key_exists($attribute['key'], $selects)) {
3355+
$relationships[] = $attribute;
3356+
}
3357+
}
3358+
}
33303359

33313360
foreach ($relationships as $relationship) {
33323361
$key = $relationship['key'];
@@ -6619,6 +6648,10 @@ protected function decodeAttribute(string $filter, mixed $value, Document $docum
66196648
return $value;
66206649
}
66216650

6651+
if (!\is_null($this->disabledFilters) && isset($this->disabledFilters[$filter])) {
6652+
return $value;
6653+
}
6654+
66226655
if (!array_key_exists($filter, self::$filters) && !array_key_exists($filter, $this->instanceFilters)) {
66236656
throw new NotFoundException("Filter \"{$filter}\" not found for attribute \"{$attribute}\"");
66246657
}

0 commit comments

Comments
 (0)