Skip to content

Operators limits#911

Open
fogelito wants to merge 18 commits into
mainfrom
operators-max-param
Open

Operators limits#911
fogelito wants to merge 18 commits into
mainfrom
operators-max-param

Conversation

@fogelito

@fogelito fogelito commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • Bug Fixes
    • Numeric updates (increment/decrement/multiply/divide/power) now avoid clamping to min/max; when an operation would exceed bounds, the existing stored value is preserved across adapters.
    • Bounded and unbounded power handling was made safer, including clearer behavior for undefined inputs (e.g., zero/negative base edge cases) and cross-database overflow/invalid cases.
    • Array operators now enforce a consistent payload size limit and apply more reliable validation for arrayFilter conditions.
    • Limit and invalid-argument errors are now mapped more accurately when range constraints are violated.

fogelito added 2 commits July 1, 2026 08:52
…max-param2

# Conflicts:
#	src/Database/Adapter/MariaDB.php
Comment thread src/Database/Adapter/Postgres.php Outdated
Comment thread src/Database/Adapter/SQLite.php Outdated
Comment thread src/Database/Adapter/MariaDB.php Outdated
Comment thread src/Database/Adapter/SQLite.php
Comment thread src/Database/Adapter/MariaDB.php Outdated
@utopia-php utopia-php deleted a comment from greptile-apps Bot Jul 1, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Database/Adapter/MariaDB.php`:
- Line 2036: The MariaDB adapter’s POWER(COALESCE(...), :$bindKey) branch can
still evaluate to NULL for invalid exponent/base combinations, causing the
update path to overwrite the column with NULL. Update the MariaDB adapter logic
around this POWER expression to guard against these cases by preserving the
existing column value when the power result would be NULL, and add coverage for
zero and negative-base/fractional-exponent scenarios in the same update flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 996433f1-7d7f-4130-9836-989ff5446fb6

📥 Commits

Reviewing files that changed from the base of the PR and between 0ffcaa6 and 3fd8f84.

📒 Files selected for processing (10)
  • src/Database/Adapter/MariaDB.php
  • src/Database/Adapter/Memory.php
  • src/Database/Adapter/Mongo.php
  • src/Database/Adapter/MySQL.php
  • src/Database/Adapter/Postgres.php
  • src/Database/Adapter/Redis.php
  • src/Database/Adapter/SQL.php
  • src/Database/Adapter/SQLite.php
  • src/Database/Operator.php
  • tests/e2e/Adapter/Scopes/OperatorTests.php
✅ Files skipped from review due to trivial changes (1)
  • src/Database/Adapter/SQL.php
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/Database/Adapter/Memory.php
  • src/Database/Adapter/Redis.php
  • src/Database/Adapter/Mongo.php
  • src/Database/Adapter/SQLite.php

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Database/Adapter/MariaDB.php`:
- Line 2036: The MariaDB adapter’s POWER(COALESCE(...), :$bindKey) branch can
still evaluate to NULL for invalid exponent/base combinations, causing the
update path to overwrite the column with NULL. Update the MariaDB adapter logic
around this POWER expression to guard against these cases by preserving the
existing column value when the power result would be NULL, and add coverage for
zero and negative-base/fractional-exponent scenarios in the same update flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 996433f1-7d7f-4130-9836-989ff5446fb6

📥 Commits

Reviewing files that changed from the base of the PR and between 0ffcaa6 and 3fd8f84.

📒 Files selected for processing (10)
  • src/Database/Adapter/MariaDB.php
  • src/Database/Adapter/Memory.php
  • src/Database/Adapter/Mongo.php
  • src/Database/Adapter/MySQL.php
  • src/Database/Adapter/Postgres.php
  • src/Database/Adapter/Redis.php
  • src/Database/Adapter/SQL.php
  • src/Database/Adapter/SQLite.php
  • src/Database/Operator.php
  • tests/e2e/Adapter/Scopes/OperatorTests.php
✅ Files skipped from review due to trivial changes (1)
  • src/Database/Adapter/SQL.php
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/Database/Adapter/Memory.php
  • src/Database/Adapter/Redis.php
  • src/Database/Adapter/Mongo.php
  • src/Database/Adapter/SQLite.php
🛑 Comments failed to post (1)
src/Database/Adapter/MariaDB.php (1)

2036-2036: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect power-related e2e tests to see base values used against the unbounded branch
rg -nP -C4 'Operator::power\(' tests/e2e/Adapter/Scopes/OperatorTests.php

Repository: utopia-php/database

Length of output: 6550


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the MariaDB power handling around the referenced line and nearby branches.
ast-grep outline src/Database/Adapter/MariaDB.php --view expanded | sed -n '1,240p'
printf '\n---\n'
sed -n '2000,2065p' src/Database/Adapter/MariaDB.php

Repository: utopia-php/database

Length of output: 4437


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect power-related operator tests around edge cases and any parity assertions.
rg -nC 3 'Operator::power\(|zero base|negative exponent|fractional exponent|undefined|NULL' tests/e2e/Adapter/Scopes/OperatorTests.php

Repository: utopia-php/database

Length of output: 8402


🌐 Web query:

MariaDB POWER negative base fractional exponent NULL documentation

💡 Result:

In MariaDB, the POWER(X, Y) function—which is a synonym for POW(X, Y)—behaves in the following ways regarding negative bases, fractional exponents, and NULL inputs: Negative Base with Fractional Exponent: When you raise a negative base to a non-integer (fractional) exponent, the result is mathematically complex (imaginary). Because MariaDB does not support complex numbers in its standard numeric functions, it returns NULL for these operations [1][2]. For example, POWER(-2, 1.5) returns NULL [1]. NULL Inputs: If either the base (X) or the exponent (Y) is NULL, the POWER function returns NULL [1][3][2]. Handling These Cases: To avoid these NULL results, you can implement checks using SQL functions: - To handle negative bases, you can use the ABS function to perform the calculation on the absolute value, or use a CASE statement to validate the inputs before execution [1][2]. - To handle NULL values, you can use COALESCE or IFNULL to provide a default value [1][2]. For example, to safely handle a potentially NULL exponent, you might use: SELECT POWER(10, COALESCE(my_exponent, 0)); [1]

Citations:


🌐 Web query:

MariaDB POWER 0 negative exponent NULL documentation

💡 Result:

In MariaDB, the POWER(X, Y) function (which is a synonym for POW(X, Y)) calculates X raised to the power of Y [1][2][3]. Regarding the specific case of POWER(0, negative_exponent), MariaDB typically returns a result based on floating-point arithmetic rather than returning NULL. In mathematical terms, raising zero to a negative power is undefined (division by zero), and MariaDB will generally return NULL or an error/warning depending on the specific environment configuration and version, as it is a domain error [4][3][5]. Documentation and general behavior summary: 1. NULL Handling: If either the base or the exponent is NULL, the POWER function returns NULL [4][6]. 2. Negative Exponents: When the base is a positive non-zero number, negative exponents are handled correctly (e.g., POWER(2, -2) returns 0.25) [2][7]. 3. Base 0 and Negative Exponent: This is a mathematical domain error. While the SQL standard suggests raising an exception for this case, MariaDB's implementation may return NULL for such undefined operations [4][5]. To handle potential issues safely, you can use COALESCE to manage NULL inputs [4] or a CASE statement to validate your inputs before passing them to the POWER function [4][3].

Citations:


Guard the unbounded power path

POWER(COALESCE({$quotedColumn}, 0), :$bindKey) can still return NULL for 0^negative or negative bases with fractional exponents on MariaDB, so this branch can write NULL back to the column instead of preserving the current value. The current tests only cover positive bases.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Database/Adapter/MariaDB.php` at line 2036, The MariaDB adapter’s
POWER(COALESCE(...), :$bindKey) branch can still evaluate to NULL for invalid
exponent/base combinations, causing the update path to overwrite the column with
NULL. Update the MariaDB adapter logic around this POWER expression to guard
against these cases by preserving the existing column value when the power
result would be NULL, and add coverage for zero and
negative-base/fractional-exponent scenarios in the same update flow.

Comment thread src/Database/Validator/Operator.php Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Database/Validator/Operator.php`:
- Around line 153-167: Count the normalized array payload in Operator::parse()
instead of the raw top-level $values array, so nested inputs like a single
wrapped list are still capped correctly. Update the array-operator guard to use
the same normalized count logic and include DatabaseOperator::TYPE_ARRAY_REMOVE
alongside TYPE_ARRAY_APPEND, TYPE_ARRAY_PREPEND, TYPE_ARRAY_INTERSECT, and
TYPE_ARRAY_DIFF so arrayRemove() is limited the same way.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2f72bd7e-050c-439c-9681-a34b00d96dae

📥 Commits

Reviewing files that changed from the base of the PR and between 3fd8f84 and c9db3ae.

📒 Files selected for processing (7)
  • src/Database/Adapter/MariaDB.php
  • src/Database/Adapter/MySQL.php
  • src/Database/Adapter/Postgres.php
  • src/Database/Adapter/SQLite.php
  • src/Database/Operator.php
  • src/Database/Validator/Operator.php
  • tests/e2e/Adapter/Scopes/OperatorTests.php
💤 Files with no reviewable changes (4)
  • src/Database/Adapter/MySQL.php
  • src/Database/Adapter/Postgres.php
  • src/Database/Adapter/SQLite.php
  • src/Database/Adapter/MariaDB.php
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Database/Operator.php

Comment thread src/Database/Validator/Operator.php

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Database/Adapter/Mongo.php (1)

1896-1905: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Don't short-circuit every bounded power when the base is <= 1.

This turns valid bounded powers into no-ops. For example, 0.5 ^ 2 with max = 1 should become 0.25, and (-4) ^ 2 with max = 20 should become 16, but this branch returns the original base in both cases. Only bypass $pow for truly undefined inputs; otherwise compare the computed result against the bound.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Database/Adapter/Mongo.php` around lines 1896 - 1905, The bounded power
logic in Mongo::convertExpression incorrectly short-circuits all cases where the
base is <= 1, causing valid computations in the power-handling branch to return
the original base instead of the bounded result. Update the condition inside the
$cond block in Mongo::convertExpression so it only skips $pow for truly
undefined inputs (such as invalid fractional/negative-power cases), and
otherwise always compare the computed $expr against $values[1] before deciding
whether to cap it or keep it.
♻️ Duplicate comments (1)
src/Database/Validator/Operator.php (1)

155-165: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Cap the normalized array payload here too.

count($values) only sees the outer container, so wrapped payloads can still bypass this limit, and TYPE_ARRAY_REMOVE is still excluded even though its validation path already normalizes values[0] later in the method. Reuse that normalized shape here and apply the same cap to remove operations as well.

Suggested fix
+        $arrayOperatorValues = \is_array($values[0] ?? null) ? $values[0] : $values;
         if (
             \in_array($method, [
                 DatabaseOperator::TYPE_ARRAY_APPEND,
                 DatabaseOperator::TYPE_ARRAY_PREPEND,
                 DatabaseOperator::TYPE_ARRAY_INTERSECT,
                 DatabaseOperator::TYPE_ARRAY_DIFF,
+                DatabaseOperator::TYPE_ARRAY_REMOVE,
             ], true)
-            && \count($values) > DatabaseOperator::MAX_ARRAY_OPERATOR_SIZE
+            && \count($arrayOperatorValues) > DatabaseOperator::MAX_ARRAY_OPERATOR_SIZE
         ) {
-            $this->message = "Array size " . \count($values) . " exceeds maximum allowed size of " . DatabaseOperator::MAX_ARRAY_OPERATOR_SIZE . " for array operations";
+            $this->message = "Array size " . \count($arrayOperatorValues) . " exceeds maximum allowed size of " . DatabaseOperator::MAX_ARRAY_OPERATOR_SIZE . " for array operations";
             return false;
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Database/Validator/Operator.php` around lines 155 - 165, The array-size
guard in Operator::validate currently checks only count($values) and omits
TYPE_ARRAY_REMOVE, so wrapped payloads can bypass the limit. Update the
validation to use the normalized array shape already used later in the method
(including values[0] for wrapped payloads) and apply the same
MAX_ARRAY_OPERATOR_SIZE cap to TYPE_ARRAY_REMOVE as well as the other array
operators.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/Database/Adapter/Mongo.php`:
- Around line 1896-1905: The bounded power logic in Mongo::convertExpression
incorrectly short-circuits all cases where the base is <= 1, causing valid
computations in the power-handling branch to return the original base instead of
the bounded result. Update the condition inside the $cond block in
Mongo::convertExpression so it only skips $pow for truly undefined inputs (such
as invalid fractional/negative-power cases), and otherwise always compare the
computed $expr against $values[1] before deciding whether to cap it or keep it.

---

Duplicate comments:
In `@src/Database/Validator/Operator.php`:
- Around line 155-165: The array-size guard in Operator::validate currently
checks only count($values) and omits TYPE_ARRAY_REMOVE, so wrapped payloads can
bypass the limit. Update the validation to use the normalized array shape
already used later in the method (including values[0] for wrapped payloads) and
apply the same MAX_ARRAY_OPERATOR_SIZE cap to TYPE_ARRAY_REMOVE as well as the
other array operators.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b1f21c55-6eb2-4281-92a9-0363c609ec91

📥 Commits

Reviewing files that changed from the base of the PR and between c9db3ae and a172cfa.

📒 Files selected for processing (6)
  • src/Database/Adapter/Memory.php
  • src/Database/Adapter/Mongo.php
  • src/Database/Adapter/Postgres.php
  • src/Database/Adapter/Redis.php
  • src/Database/Validator/Operator.php
  • tests/e2e/Adapter/Scopes/OperatorTests.php
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/Database/Adapter/Postgres.php
  • src/Database/Adapter/Memory.php
  • src/Database/Adapter/Redis.php
  • tests/e2e/Adapter/Scopes/OperatorTests.php

Comment thread tests/e2e/Adapter/Scopes/OperatorTests.php Outdated
@utopia-php utopia-php deleted a comment from coderabbitai Bot Jul 1, 2026
@utopia-php utopia-php deleted a comment from greptile-apps Bot Jul 1, 2026
@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR changes the semantics of bounded numeric operators from "clamp to the limit value when an operation would exceed it" to "leave the stored value unchanged when an operation would cross the configured min/max." The change applies consistently across MariaDB, Postgres, SQLite, MySQL, MongoDB, Memory, and Redis adapters. Additionally, array operator size enforcement is lifted out of the individual SQL adapters into the shared Validator/Operator layer, and POWER edge cases (zero/negative base with fractional or negative exponent) are handled safely across every adapter.

  • Bounded operator semantics: all numeric operators (increment/decrement/multiply/divide/power) now preserve the existing value rather than clamping when a result would exceed the limit; POWER gains per-adapter guards for mathematically undefined inputs (0^negative, negative^fractional) so the database never receives an ill-defined expression.
  • Validator-level array cap: MAX_ARRAY_OPERATOR_SIZE and ARRAY_FILTER_CONDITIONS are promoted to public constants on Operator, and the size/condition validation is consolidated into Validator/Operator so every adapter rejects an oversized array identically via StructureException.

Confidence Score: 5/5

The PR is safe to merge. All seven adapters now apply bounded operators consistently, and the new POWER guards prevent hard database errors for undefined inputs across every engine.

The odd-integer exponent path correctly lets negative-base results pass through to POWER() rather than being blocked by the ABS log comparison; the leave-unchanged guard arithmetic is equivalent across adapters (with Postgres using arbitrary-precision NUMERIC to sidestep overflow where MariaDB uses division); the validator-layer array size cap covers all adapters identically; and the new tests exercise inclusive bounds, per-column and per-row isolation, shrink-applies on already-over-limit bases, and each mathematically undefined power combination.

No files require special attention.

Important Files Changed

Filename Overview
src/Database/Adapter/MariaDB.php Numeric bounds now leave values unchanged instead of clamping; POWER guard correctly emits only the needed undefined guards and uses the odd-integer path for negative bases so they fall through to POWER() rather than being blocked by the ABS log check. Array size checks removed (moved to validator).
src/Database/Adapter/Postgres.php All numeric bound guards updated to leave-unchanged semantics. INCREMENT/DECREMENT use direct arithmetic (safe because of NUMERIC cast). MULTIPLY uses direct multiplication with NUMERIC (arbitrary precision, no overflow risk). POWER guard mirrors MariaDB with odd-integer optimisation and undefined-input guards. Error code 2201F is now mapped to LimitException.
src/Database/Adapter/SQLite.php Bounded guards updated to leave-unchanged semantics and POWER guard added matching MariaDB/Postgres logic. Array size checks removed (moved to validator).
src/Database/Adapter/Mongo.php Bounded operators switched from $min/$max to $cond(result <= bound, result, original) so Mongo aligns with the new leave-unchanged semantics. POWER adds undefined-input guards for negative exponent on zero base and fractional exponent on negative base. Error code 28764 is now mapped to LimitException.
src/Database/Adapter/Memory.php All numeric bound guards rewritten to leave-unchanged using headroom comparisons. POWER handles undefined inputs with early return and throws LimitException for infinite results in the unbounded path.
src/Database/Adapter/Redis.php Mirrors Memory.php changes exactly, leave-unchanged semantics for all bounded numeric operators and LimitException for unbounded undefined power results.
src/Database/Adapter/SQL.php MAX_ARRAY_OPERATOR_SIZE constant removed (promoted to Operator.php as a public constant).
src/Database/Adapter/MySQL.php Array size checks removed from ARRAY_APPEND and ARRAY_PREPEND (moved to validator layer).
src/Database/Operator.php Adds public constants MAX_ARRAY_OPERATOR_SIZE (10000) and ARRAY_FILTER_CONDITIONS so they can be shared between adapters and the validator without the validator depending on a specific adapter class.
src/Database/Validator/Operator.php Array size enforcement and filter-condition validation consolidated here using shared constants; removal of the adapter-side duplicates means every path now validates identically before dispatching.
tests/e2e/Adapter/Scopes/OperatorTests.php Existing assertions updated to reflect leave-unchanged semantics. New tests cover: shrink-applies on bounded operators, per-column and per-row guard isolation, inclusive-boundary verification, POWER on undefined bases (bounded and unbounded), array size limit enforcement, and all arrayFilter conditions.

Reviews (14): Last reviewed commit: "Address greptile review" | Re-trigger Greptile

Comment thread src/Database/Adapter/MariaDB.php Outdated
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • ✅ Review completed - (🔄 Check again to review again)

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d03cfc5b-36a8-42ba-a8f5-cb4a7ae4aac3

📥 Commits

Reviewing files that changed from the base of the PR and between a172cfa and db28249.

📒 Files selected for processing (8)
  • src/Database/Adapter/MariaDB.php
  • src/Database/Adapter/Memory.php
  • src/Database/Adapter/Mongo.php
  • src/Database/Adapter/Postgres.php
  • src/Database/Adapter/Redis.php
  • src/Database/Adapter/SQLite.php
  • src/Database/Validator/Operator.php
  • tests/e2e/Adapter/Scopes/OperatorTests.php
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/Database/Validator/Operator.php
  • src/Database/Adapter/Mongo.php

📝 Walkthrough

Walkthrough

Numeric operator bound handling was changed across MariaDB, Postgres, SQLite, Mongo, Memory, and Redis adapters to return the existing/original value instead of clamping to min/max when a bound would be exceeded, with new power-overflow and undefined-case guards. Array operator size limits were consolidated into a shared Operator::MAX_ARRAY_OPERATOR_SIZE constant used by the validator instead of per-adapter checks, and array-filter condition validation now uses a shared constant. Tests were updated to reflect unchanged-value semantics on bound breaches and to add new limit/edge-case coverage.

Changes

Operator bound and size-limit updates

Layer / File(s) Summary
Shared array size limit and validation
src/Database/Operator.php, src/Database/Validator/Operator.php, src/Database/Adapter/SQL.php, src/Database/Adapter/MySQL.php, src/Database/Adapter/MariaDB.php, src/Database/Adapter/SQLite.php, src/Database/Adapter/Postgres.php
Adds Operator::MAX_ARRAY_OPERATOR_SIZE, moves the size check into the validator (covering array append/prepend/intersect/diff/remove), removes per-adapter count checks and array-filter condition allowlists, and updates the Operator::ARRAY_FILTER_CONDITIONS-based validation message.
SQL adapter numeric bound guards
src/Database/Adapter/MariaDB.php, src/Database/Adapter/Postgres.php, src/Database/Adapter/SQLite.php
Increment/decrement/multiply/divide bounded SQL now returns the current column value on overflow/underflow instead of clamping; power bounded SQL uses logarithm-based overflow checks with undefined-case guards; Postgres maps error code 2201F to a limit exception.
Mongo numeric bound guards
src/Database/Adapter/Mongo.php
getOperatorExpression() falls back to the original field value on bound violation for increment/decrement/multiply/divide/power, adds power undefined-case guards, and processException() maps error code 28764 to a limit exception.
Memory and Redis numeric guards
src/Database/Adapter/Memory.php, src/Database/Adapter/Redis.php
Replaces applyNumericLimit() clamping with explicit checks that return the original base value on bound violation, adds power undefined/overflow handling, and throws a limit exception for non-finite unbounded power results.
Operator test suite updates
tests/e2e/Adapter/Scopes/OperatorTests.php
Replaces broad exception assertions with specific exception types, updates bound-breach expectations to "unchanged value," and adds new tests for shrink-into-range, guard scope, inclusive bounds, power edge cases, and array size limits.

Estimated code review effort: 4 (Complex) | ~75 minutes

Possibly related PRs

  • utopia-php/database#713: Both PRs touch array-size limiting scaffolding and its relocation/validation across adapters.
  • utopia-php/database#907: Both PRs modify Mongo.php operator expression generation and array filter handling.
  • utopia-php/database#909: Both PRs modify getOperatorSQL()/operator-SQL generation in MariaDB, Postgres, and SQLite adapters.

Suggested reviewers: abnegate

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is related to the changes, but it is too vague to describe the main update clearly. Use a more specific title like "Tighten operator bound handling and array validation".
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch operators-max-param

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Database/Adapter/MariaDB.php`:
- Around line 2039-2057: The bounded power handling in MariaDBAdapter’s power
update path does not special-case a zero exponent, so `Operator::power(0, $max)`
can still fall through to `POWER(0, 0)` and write `1` when `$max < 1`. Update
the CASE-building logic in the MariaDB `power`/bounded exponent branch to add an
explicit zero-exponent guard before the log-based overflow checks, ensuring the
existing column value is preserved for `0^0` instead of letting `POWER()`
decide.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1ae16548-2b57-4d8b-8283-f085e148c5f4

📥 Commits

Reviewing files that changed from the base of the PR and between a172cfa and db28249.

📒 Files selected for processing (8)
  • src/Database/Adapter/MariaDB.php
  • src/Database/Adapter/Memory.php
  • src/Database/Adapter/Mongo.php
  • src/Database/Adapter/Postgres.php
  • src/Database/Adapter/Redis.php
  • src/Database/Adapter/SQLite.php
  • src/Database/Validator/Operator.php
  • tests/e2e/Adapter/Scopes/OperatorTests.php
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/Database/Validator/Operator.php
  • src/Database/Adapter/Mongo.php
  • src/Database/Adapter/Redis.php
  • src/Database/Adapter/Postgres.php
  • src/Database/Adapter/Memory.php
  • src/Database/Adapter/SQLite.php
  • tests/e2e/Adapter/Scopes/OperatorTests.php

Comment on lines +2039 to +2057
if (\floor($exponent) != $exponent) {
// A negative base to a fractional exponent is not a real number.
$whens[] = "WHEN {$col} < 0 THEN {$col}";
}
// Cap by magnitude via logarithms so POWER() never runs on a value that would
// overflow (base^exp > max <=> exp * LOG(base) > LOG(max)).
if ($oddInteger) {
// An odd exponent keeps a negative base negative, and a negative result is
// always within a positive max, so only cap positive bases; negative bases
// fall through to POWER() and their (negative) result is applied.
$whens[] = "WHEN {$col} > 0 AND :$bindKey * LOG({$col}) > LOG(:$maxKey) THEN {$col}";
} else {
// Otherwise the result is non-negative, so its magnitude equals its value —
// cap either sign. ABS() keeps LOG() defined for a negative even-power base.
$whens[] = "WHEN {$col} <> 0 AND :$bindKey * LOG(ABS({$col})) > LOG(:$maxKey) THEN {$col}";
}

$whenSql = \implode(' ', $whens);
return "{$quotedColumn} = CASE {$whenSql} ELSE POWER({$col}, :$bindKey) END";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether tests cover bounded zero-to-zero power with max < 1.
rg -n "Operator::power\(0|bounded_power|0\.5" tests src

Repository: utopia-php/database

Length of output: 7762


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the MariaDB power implementation around the cited lines.
file="src/Database/Adapter/MariaDB.php"
wc -l "$file"
sed -n '2000,2085p' "$file"

# Inspect operator tests around power/bounded power cases.
testfile="tests/e2e/Adapter/Scopes/OperatorTests.php"
wc -l "$testfile"
sed -n '1720,1775p' "$testfile"
sed -n '4035,4075p' "$testfile"
sed -n '1415,1475p' "$testfile"

Repository: utopia-php/database

Length of output: 12896


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for any bounded power test using exponent 0, and inspect the createAttribute max constraint semantics.
rg -n "Operator::power\(0,|power\(0, [^)]*\)|Operator::power\(0\)" tests/e2e/Adapter/Scopes/OperatorTests.php tests/unit/OperatorTest.php src/Database/Adapter/MariaDB.php

# Inspect the operator constructor/shape if needed to understand default arguments.
rg -n "function power|TYPE_POWER|power\(" src tests/unit tests/e2e -g '!vendor'

Repository: utopia-php/database

Length of output: 6377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="src/Database/Validator/Operator.php"
wc -l "$file"
sed -n '160,240p' "$file"

Repository: utopia-php/database

Length of output: 4822


Handle 0^0 in the bounded power path (src/Database/Adapter/MariaDB.php:2039-2057).
Operator::power(0, $max) falls through to POWER(0, 0) for a base of 0, so when $max < 1 it writes 1 instead of keeping the existing value. Add a zero-exponent guard before the log-based check.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Database/Adapter/MariaDB.php` around lines 2039 - 2057, The bounded power
handling in MariaDBAdapter’s power update path does not special-case a zero
exponent, so `Operator::power(0, $max)` can still fall through to `POWER(0, 0)`
and write `1` when `$max < 1`. Update the CASE-building logic in the MariaDB
`power`/bounded exponent branch to add an explicit zero-exponent guard before
the log-based overflow checks, ensuring the existing column value is preserved
for `0^0` instead of letting `POWER()` decide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant