Skip to content

Operators binds#909

Merged
abnegate merged 5 commits into
mainfrom
operators-binds
Jun 30, 2026
Merged

Operators binds#909
abnegate merged 5 commits into
mainfrom
operators-binds

Conversation

@fogelito

@fogelito fogelito commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of database update and upsert operations for advanced field operators, making these actions more reliable across supported databases.
    • Added stricter validation for array-based operations and condition values, helping prevent invalid queries and unexpected failures.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

An error occurred during the review process. Please try again later.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch operators-binds

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.

@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR refactors how operator bind parameters are managed across all SQL adapters. Instead of a two-pass approach — first generating SQL with integer-indexed placeholders (op_0, op_1, …) and then binding values in a separate bindOperatorParams() call that had to replicate the same ordering logic — SQL and binds are now produced together in getOperatorSQL() via the new registerOperatorBind() helper. The old bindOperatorParams() methods are deleted in full.

  • SQL::registerOperatorBind() atomically generates a unique placeholder key (via ID::unique()), stores the value in a caller-supplied $binds map, and returns the key for immediate interpolation into the SQL fragment, making SQL/bind drift structurally impossible.
  • ARRAY_APPEND, ARRAY_PREPEND, ARRAY_INTERSECT, and ARRAY_DIFF size validation is moved from the old base-class bindOperatorParams() into each adapter's getOperatorSQL(), and the ARRAY_FILTER condition whitelist check is similarly inlined for MariaDB and Postgres.

Confidence Score: 5/5

Safe to merge; the refactor is mechanical and the new design eliminates the class of bugs where SQL placeholder order and binding order could diverge.

Every SQL adapter now generates placeholder names and registers their values in one atomic call, removing the fragile two-pass counter that the old code required. The removed bindOperatorParams() overrides have been verified to be fully covered by the new getOperatorSQL() logic. Existing size guards and condition whitelists are preserved or moved earlier (now throwing before SQL is even built). No logic regressions were found.

No files require special attention.

Important Files Changed

Filename Overview
src/Database/Adapter/SQL.php Core change: replaces bindOperatorParams() with registerOperatorBind(), removes the integer counter thread, and wires callers to bind from the collected $operatorBinds map. The new helper is clean and correct.
src/Database/Adapter/MariaDB.php All operator cases migrated to registerOperatorBind(); size validation and ARRAY_FILTER condition whitelist added inline. ARRAY_REMOVE retains the explicit (string) cast for PARAM_STR semantics as documented by an inline comment.
src/Database/Adapter/MySQL.php Straightforward signature update and $values extraction; size validation added for ARRAY_APPEND/PREPEND overrides; delegates to parent for remaining operators.
src/Database/Adapter/Postgres.php Full bindOperatorParams() override deleted; all operator cases now call registerOperatorBind() and bind from $operatorBinds. ARRAY_FILTER adds the condition whitelist check.
src/Database/Adapter/SQLite.php Full bindOperatorParams() override deleted; ARRAY_FILTER preserves its switch-based structure with the count($values) < 2 guard before accessing $values[1]; all other operator cases correctly call registerOperatorBind().

Reviews (3): Last reviewed commit: "Revert greptile comments" | Re-trigger Greptile

Comment thread src/Database/Adapter/SQL.php
Comment thread src/Database/Adapter/MariaDB.php
Comment thread 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

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

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

2169-2179: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve JSON scalar types in ARRAY_REMOVE. Casting every non-array operand to string prevents SQLite from matching numeric and boolean JSON values, and null needs its own IS NOT NULL branch. Keep numbers/booleans typed and special-case null.

🤖 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/SQLite.php` around lines 2169 - 2179, The ARRAY_REMOVE
handling in SQLite::compileOperator currently string-casts every non-array
value, which breaks matching for numeric and boolean JSON scalars and does not
handle null correctly. Update the TYPE_ARRAY_REMOVE branch to preserve the
operand’s native JSON scalar type when binding, and add a dedicated null path so
the json_each() filter uses an IS NOT NULL-style condition instead of comparing
against a bound string. Use the existing SQLite::registerOperatorBind flow and
the TYPE_ARRAY_REMOVE case to locate and adjust the logic.
🤖 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/SQLite.php`:
- Around line 2169-2179: The ARRAY_REMOVE handling in SQLite::compileOperator
currently string-casts every non-array value, which breaks matching for numeric
and boolean JSON scalars and does not handle null correctly. Update the
TYPE_ARRAY_REMOVE branch to preserve the operand’s native JSON scalar type when
binding, and add a dedicated null path so the json_each() filter uses an IS NOT
NULL-style condition instead of comparing against a bound string. Use the
existing SQLite::registerOperatorBind flow and the TYPE_ARRAY_REMOVE case to
locate and adjust the logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e607a877-8811-475e-9c34-c1fdd70bd97a

📥 Commits

Reviewing files that changed from the base of the PR and between 6d5a86b and edf26f7.

📒 Files selected for processing (5)
  • src/Database/Adapter/MariaDB.php
  • src/Database/Adapter/MySQL.php
  • src/Database/Adapter/Postgres.php
  • src/Database/Adapter/SQL.php
  • src/Database/Adapter/SQLite.php

fogelito and others added 3 commits June 30, 2026 14:50
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@abnegate abnegate merged commit 7bbe954 into main Jun 30, 2026
21 checks passed
@abnegate abnegate deleted the operators-binds branch June 30, 2026 13:07
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.

2 participants