Skip to content

Conversation

@ArnabChatterjee20k
Copy link
Contributor

@ArnabChatterjee20k ArnabChatterjee20k commented Aug 27, 2025

Summary by CodeRabbit

  • Bug Fixes
    • Enforced stricter validation for spatial attributes (point/linestring/polygon) on create and update: size and array must be empty; invalid default shapes now raise clear errors.
    • Spatial defaults must be provided as a single array value.
    • Prevented creation of spatial indexes on nullable attributes when the backend disallows nulls.
  • Tests
    • Added end-to-end tests for spatial attribute validation, default behavior, and adapter-specific spatial index capabilities.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 27, 2025

Warning

Rate limit exceeded

@ArnabChatterjee20k has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 0 minutes and 39 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between df7902c and c98b33b.

📒 Files selected for processing (1)
  • src/Database/Database.php (4 hunks)

Walkthrough

This change tightens spatial attribute validation and default handling in Database.php, applies the same spatial checks on updates, adds a post-update spatial-index nullability check when the adapter lacks support, and introduces two adapter-aware end-to-end spatial tests.

Changes

Cohort / File(s) Summary
Spatial attribute validation and index constraints
src/Database/Database.php
- validateAttribute: enforce spatial support; require empty size and empty array for point/linestring/polygon.
- validateDefaultTypes: treat spatial defaults as a single array value (no per-element recursion); non-spatial recursive validation unchanged.
- updateAttribute: mirror creation-time spatial validation; after update, if adapter lacks SpatialIndexNull, scan collection indexes and throw IndexException if any spatial-indexed attribute is not required. No public API/signature changes.
E2E spatial tests
tests/e2e/Adapter/Scopes/SpatialTests.php
- Added testUpdateSpatialAttributes() to cover create/update spatial validation, required-flag interaction with spatial-index-null support, and index order behavior (adapter-gated).
- Added testSpatialAttributeDefaults() to verify default handling for spatial vs non-spatial, default updates, and invalid default shapes. Tests guard execution by adapter capabilities; imports Utopia\Database\Exception for assertions.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant Database
  participant Adapter
  participant Indexes as CollectionIndexes

  rect rgba(200,220,255,0.18)
  note over Client,Database: Create/Update spatial attribute
  Client->>Database: createAttribute/updateAttribute(spatial)
  Database->>Adapter: checkSpatialSupport()
  Adapter-->>Database: supported / not supported
  alt spatial not supported
    Database-->>Client: AttributeException
  else supported
    Database->>Database: validate size==empty && array==empty
    Database->>Database: validateDefaultTypes (spatial default = single array)
    Database-->>Client: attribute created/updated
  end
  end

  rect rgba(255,230,200,0.18)
  note over Database,Indexes: Post-update spatial index nullability check
  Database->>Adapter: supportsSpatialIndexNull?
  Adapter-->>Database: true / false
  alt false
    Database->>Indexes: scan for spatial indexes
    Database->>Database: ensure indexed spatial attrs are required
    alt any not required
      Database-->>Client: IndexException (spatial index cannot be null)
    else
      Database-->>Client: OK
    end
  else true
    Database-->>Client: OK
  end
  end
Loading
sequenceDiagram
  autonumber
  participant Client
  participant Database

  note over Client,Database: Default validation on attribute creation
  Client->>Database: createAttribute(type, default)
  alt spatial type (point/linestring/polygon)
    Database->>Database: accept default as single array value (no per-element recursion)
    Database-->>Client: success or validation error
  else non-spatial
    Database->>Database: recursively validate array elements (if array)
    Database-->>Client: success or validation error
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • abnegate

Poem

I hop through points and lines so spry,
Polygons tidy beneath the sky.
No sizes, no arrays that roam,
Defaults snug, indexes call home.
I thump for checks — safe and spry. 🐇📐

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

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

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (7)
src/Database/Database.php (1)

1912-1917: Default validation: correct special-casing for spatial arrays.

Treating spatial defaults as a single array (no element-wise recursion) is right. Minor readability tweak below to avoid a misleading “no break” fallthrough.

Apply this diff to make intent explicit in the switch that follows:

 case self::VAR_POINT:
 case self::VAR_LINESTRING:
 case self::VAR_POLYGON:
     // Spatial types expect arrays as default values
     if ($defaultType !== 'array') {
         throw new DatabaseException('Default value for spatial type ' . $type . ' must be an array');
     }
-    // no break
+    break;
tests/e2e/Adapter/Scopes/SpatialTests.php (6)

342-349: Remove redundant branch and use assertTrue for clarity.

Both branches call the same createIndex; collapse to a single call.

Apply this diff:

-            if ($database->getAdapter()->getSupportForSpatialIndexNull()) {
-                $this->assertEquals(true, $database->createIndex($collectionName, 'idx_line', Database::INDEX_SPATIAL, ['lineAttr']));
-            } else {
-                // Attribute was created as required above; directly create index once
-                $this->assertEquals(true, $database->createIndex($collectionName, 'idx_line', Database::INDEX_SPATIAL, ['lineAttr']));
-            }
+            $this->assertTrue($database->createIndex($collectionName, 'idx_line', Database::INDEX_SPATIAL, ['lineAttr']));

1791-1796: Make collection name unique to avoid rare cross-test collisions.

Minor hardening; several tests already use fixed names, but uniqueness removes flakiness if cleanup ever fails.

-        $collectionName = 'spatial_update_attrs_';
+        $collectionName = 'spatial_update_attrs_' . uniqid();

1814-1827: DRY up repeated try/catch with a tiny helper closure.

Reduces repetition and keeps assertions focused.

You can inline this helper in the test method and reuse it:

$assertThrows = function (callable $fn): void {
    try { $fn(); $this->fail('Expected exception'); }
    catch (\Throwable $e) { $this->assertInstanceOf(Exception::class, $e); }
};

$assertThrows(fn() => $database->updateAttribute($collectionName, 'geom', size: 10));
$assertThrows(fn() => $database->updateAttribute($collectionName, 'geom', array: true));

1832-1848: Don’t assume attribute order; assert on the specific attribute by key.

Index 0 is fragile. Look up the 'geom' attribute before asserting required.

-                $database->updateAttribute($collectionName, 'geom', required: false);
-                $meta = $database->getCollection($collectionName);
-                $this->assertEquals(false, $meta->getAttribute('attributes')[0]['required']);
+                $database->updateAttribute($collectionName, 'geom', required: false);
+                $meta = $database->getCollection($collectionName);
+                $attrs = $meta->getAttribute('attributes', []);
+                $geomAttr = null;
+                foreach ($attrs as $attr) {
+                    if (($attr['key'] ?? $attr['$id']) === 'geom') { $geomAttr = $attr; break; }
+                }
+                $this->assertNotNull($geomAttr, 'geom attribute not found');
+                $this->assertFalse($geomAttr['required']);

Also assert the exception type on the non-nullable-adapter branch:

-                try {
-                    $database->updateAttribute($collectionName, 'geom', required: false);
-                } catch (\Throwable $e) {
-                    $threw = true;
-                }
+                try {
+                    $database->updateAttribute($collectionName, 'geom', required: false);
+                } catch (\Throwable $e) {
+                    $threw = true;
+                    $this->assertInstanceOf(Exception::class, $e);
+                }

1882-1891: Prefer assertTrue over assertEquals(true, …).

Improves intent/readability on boolean-returning APIs.

-            $this->assertEquals(true, $database->createAttribute($collectionName, 'pt', Database::VAR_POINT, 0, false, [1.0, 2.0]));
-            $this->assertEquals(true, $database->createAttribute($collectionName, 'ln', Database::VAR_LINESTRING, 0, false, [[0.0, 0.0], [1.0, 1.0]]));
-            $this->assertEquals(true, $database->createAttribute($collectionName, 'pg', Database::VAR_POLYGON, 0, false, [[[0.0, 0.0], [0.0, 2.0], [2.0, 2.0], [0.0, 0.0]]]));
+            $this->assertTrue($database->createAttribute($collectionName, 'pt', Database::VAR_POINT, 0, false, [1.0, 2.0]));
+            $this->assertTrue($database->createAttribute($collectionName, 'ln', Database::VAR_LINESTRING, 0, false, [[0.0, 0.0], [1.0, 1.0]]));
+            $this->assertTrue($database->createAttribute($collectionName, 'pg', Database::VAR_POLYGON, 0, false, [[[0.0, 0.0], [0.0, 2.0], [2.0, 2.0], [0.0, 0.0]]]));

1949-1967: Strengthen invalid-default assertions by checking exception type.

Avoids false positives from unrelated throwables.

-            } catch (\Throwable $e) {
-                $this->assertTrue(true);
+            } catch (\Throwable $e) {
+                $this->assertInstanceOf(Exception::class, $e);
             }
-            try {
+            try {
                 $database->updateAttributeDefault($collectionName, 'ln', [1.0, 2.0]); // wrong dimensionality
                 $this->fail('Expected exception for invalid linestring default shape');
-            } catch (\Throwable $e) {
-                $this->assertTrue(true);
+            } catch (\Throwable $e) {
+                $this->assertInstanceOf(Exception::class, $e);
             }
-            try {
+            try {
                 $database->updateAttributeDefault($collectionName, 'pg', [[1.0, 2.0]]); // wrong dimensionality
                 $this->fail('Expected exception for invalid polygon default shape');
-            } catch (\Throwable $e) {
-                $this->assertTrue(true);
+            } catch (\Throwable $e) {
+                $this->assertInstanceOf(Exception::class, $e);
             }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 99beaf1 and f2f5ee3.

📒 Files selected for processing (2)
  • src/Database/Database.php (4 hunks)
  • tests/e2e/Adapter/Scopes/SpatialTests.php (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/Database/Database.php (7)
src/Database/Adapter.php (2)
  • getSupportForSpatialAttributes (1036-1036)
  • getSupportForSpatialIndexNull (1043-1043)
src/Database/Adapter/Pool.php (2)
  • getSupportForSpatialAttributes (428-431)
  • getSupportForSpatialIndexNull (433-436)
src/Database/Adapter/MariaDB.php (2)
  • getSupportForSpatialAttributes (2157-2160)
  • getSupportForSpatialIndexNull (2167-2170)
src/Database/Adapter/SQL.php (2)
  • getSupportForSpatialAttributes (1496-1499)
  • getSupportForSpatialIndexNull (1506-1509)
src/Database/Adapter/SQLite.php (2)
  • getSupportForSpatialAttributes (970-973)
  • getSupportForSpatialIndexNull (975-978)
src/Database/Adapter/Postgres.php (2)
  • getSupportForSpatialAttributes (2261-2264)
  • getSupportForSpatialIndexNull (2271-2274)
src/Database/Document.php (1)
  • getAttribute (216-223)
tests/e2e/Adapter/Scopes/SpatialTests.php (3)
src/Database/Database.php (6)
  • Database (37-7180)
  • getAdapter (1111-1114)
  • createIndex (3118-3273)
  • updateAttribute (2139-2323)
  • getCollection (1415-1431)
  • updateAttributeDefault (2108-2119)
src/Database/Exception.php (1)
  • Exception (7-21)
src/Database/Adapter.php (5)
  • getSupportForSpatialIndexNull (1043-1043)
  • createIndex (649-649)
  • getSupportForSpatialAttributes (1036-1036)
  • updateAttribute (563-563)
  • getSupportForSpatialIndexOrder (1050-1050)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Setup & Build Docker Image
🔇 Additional comments (5)
src/Database/Database.php (2)

1855-1860: Spatial attrs: enforcing size=0 and non-array is correct.

The new checks align spatial attributes with engine capabilities and mirror other simple types’ constraints. LGTM.


2186-2199: Update path parity: spatial type checks mirror create path.

Re-validating adapter support, size=0, and non-array on update prevents drift. Looks good.

tests/e2e/Adapter/Scopes/SpatialTests.php (3)

7-7: Importing base Database exception is correct.

This enables explicit assertion of thrown exception types in the new tests.


1795-1808: Good negative coverage for spatial create() constraints.

Asserting exceptions for non-zero size and array=true on spatial types matches the tightened validations.


1851-1863: Order-support check looks solid and includes cleanup.

Covers both supported and unsupported adapters correctly.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/Database/Database.php (2)

2247-2274: Spatial-index nullability enforcement is now correct and scoped — nice fix

Checks each indexed spatial attribute’s own required flag, filters to spatial indexes, and guards missing keys. This addresses the earlier review concern.


2036-2041: Prevent toggling required=false on spatial attrs in spatial indexes when NULLs unsupported

updateAttributeRequired bypasses the new index nullability guard. Disallow making an indexed spatial attribute nullable when the adapter can’t handle NULLs.

-    public function updateAttributeRequired(string $collection, string $id, bool $required): Document
-    {
-        return $this->updateAttributeMeta($collection, $id, function ($attribute) use ($required) {
-            $attribute->setAttribute('required', $required);
-        });
-    }
+    public function updateAttributeRequired(string $collection, string $id, bool $required): Document
+    {
+        return $this->updateAttributeMeta($collection, $id, function ($attribute, $collectionDoc) use ($required) {
+            // Enforce: spatial indexes cannot include nullable spatial attributes on engines without NULL support
+            if (
+                $required === false
+                && in_array($attribute->getAttribute('type'), self::SPATIAL_TYPES, true)
+                && !$this->adapter->getSupportForSpatialIndexNull()
+            ) {
+                $indexes = $collectionDoc->getAttribute('indexes', []);
+                foreach ($indexes as $index) {
+                    if (
+                        $index->getAttribute('type') === self::INDEX_SPATIAL
+                        && in_array($attribute->getAttribute('key'), $index->getAttribute('attributes', []), true)
+                    ) {
+                        throw new IndexException('Spatial indexes do not allow null values. Mark the attribute "' . $attribute->getAttribute('key') . '" as required or drop the spatial index first.');
+                    }
+                }
+            }
+            $attribute->setAttribute('required', $required);
+        });
+    }
🧹 Nitpick comments (3)
src/Database/Database.php (3)

2200-2201: Error message should list spatial types too

Default branch now excludes spatial types from the “Must be one of …” list despite supporting them above.

- throw new DatabaseException('Unknown attribute type: ' . $type . '. Must be one of ' . self::VAR_STRING . ', ' . self::VAR_INTEGER . ', ' . self::VAR_FLOAT . ', ' . self::VAR_BOOLEAN . ', ' . self::VAR_DATETIME . ', ' . self::VAR_RELATIONSHIP);
+ throw new DatabaseException('Unknown attribute type: ' . $type . '. Must be one of ' . self::VAR_STRING . ', ' . self::VAR_INTEGER . ', ' . self::VAR_FLOAT . ', ' . self::VAR_BOOLEAN . ', ' . self::VAR_DATETIME . ', ' . self::VAR_RELATIONSHIP . ', ' . self::VAR_POINT . ', ' . self::VAR_LINESTRING . ', ' . self::VAR_POLYGON);

2224-2234: Minor: method name casing

Use setAttribute (current casing is setattribute). PHP is case-insensitive here, but consistency helps static analysis and readability.

-                ->setattribute('key', $newKey ?? $id)
+                ->setAttribute('key', $newKey ?? $id)

1912-1914: Consistency: prefer self::SPATIAL_TYPES inside the class

Tiny style nit: use self::SPATIAL_TYPES (as done elsewhere) instead of Database::SPATIAL_TYPES.

-            if (!in_array($type, Database::SPATIAL_TYPES)) {
+            if (!in_array($type, self::SPATIAL_TYPES, true)) {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f2f5ee3 and df7902c.

📒 Files selected for processing (1)
  • src/Database/Database.php (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/Database/Database.php (7)
src/Database/Adapter.php (2)
  • getSupportForSpatialAttributes (1036-1036)
  • getSupportForSpatialIndexNull (1043-1043)
src/Database/Adapter/MariaDB.php (2)
  • getSupportForSpatialAttributes (2157-2160)
  • getSupportForSpatialIndexNull (2167-2170)
src/Database/Adapter/Pool.php (2)
  • getSupportForSpatialAttributes (428-431)
  • getSupportForSpatialIndexNull (433-436)
src/Database/Adapter/Postgres.php (2)
  • getSupportForSpatialAttributes (2261-2264)
  • getSupportForSpatialIndexNull (2271-2274)
src/Database/Adapter/SQL.php (2)
  • getSupportForSpatialAttributes (1496-1499)
  • getSupportForSpatialIndexNull (1506-1509)
src/Database/Adapter/SQLite.php (2)
  • getSupportForSpatialAttributes (970-973)
  • getSupportForSpatialIndexNull (975-978)
src/Database/Document.php (1)
  • getAttribute (216-223)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Adapter Tests (SharedTables/SQLite)
  • GitHub Check: Adapter Tests (SharedTables/Postgres)
  • GitHub Check: Adapter Tests (Pool)
  • GitHub Check: Adapter Tests (SharedTables/MariaDB)
  • GitHub Check: Adapter Tests (Mirror)
  • GitHub Check: Adapter Tests (SharedTables/MySQL)
  • GitHub Check: Adapter Tests (Postgres)
  • GitHub Check: Adapter Tests (MySQL)
  • GitHub Check: Adapter Tests (SQLite)
  • GitHub Check: Adapter Tests (MariaDB)
🔇 Additional comments (2)
src/Database/Database.php (2)

1855-1860: Spatial attribute creation: size/array guards look good

Correctly enforces size=0 and array=false for spatial attributes.


2186-2199: Spatial attribute update: parity with creation checks — good

Mirrors create-time validation (support check, size=0, array=false). Looks consistent.

Co-authored-by: Jake Barnby <jakeb994@gmail.com>
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
@abnegate abnegate merged commit d59a207 into utopia-php:main Aug 27, 2025
15 checks passed
This was referenced Sep 11, 2025
@coderabbitai coderabbitai bot mentioned this pull request Sep 26, 2025
Merged
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