Skip to content

Fix SQLite row conversion error in toggle-favourite endpoint#1211

Closed
Riddhi8077 wants to merge 10 commits intoAOSSIE-Org:mainfrom
Riddhi8077:optimize-toggle-favourite-query
Closed

Fix SQLite row conversion error in toggle-favourite endpoint#1211
Riddhi8077 wants to merge 10 commits intoAOSSIE-Org:mainfrom
Riddhi8077:optimize-toggle-favourite-query

Conversation

@Riddhi8077
Copy link
Copy Markdown

@Riddhi8077 Riddhi8077 commented Mar 5, 2026

Summary

This PR fixes an issue where the /images/toggle-favourite endpoint raised an Internal Server Error when attempting to convert SQLite query results into dictionaries.

The problem occurred because SQLite rows were returned as tuples, which caused an error when using dict(row).

The fix sets the SQLite connection's row_factory to sqlite3.Row, allowing rows to behave like dictionary objects and enabling safe conversion using dict(row).

Problem

The /images/toggle-favourite endpoint attempted to convert database rows to dictionaries using:

dict(row)

However, the SQLite connection did not use a row factory that supports dictionary-style access. As a result, rows were returned as tuples, which caused the following error:

dictionary update sequence element #0 has length 1; 2 is required

This resulted in the endpoint returning an Internal Server Error.

Solution

To resolve this issue, the SQLite connection configuration was updated to include:

conn.row_factory = sqlite3.Row

This change ensures that query results are returned as dictionary-like objects instead of tuples.

As a result, rows can now be safely converted using dict(row) without raising errors, and the endpoint functions as expected.

Changes Made

  • Updated SQLite connection configuration to use sqlite3.Row as the row factory
  • Ensured database rows support dictionary-style access
  • Prevented errors when converting rows using dict(row)
  • Improved reliability of the /images/toggle-favourite endpoint

Testing

The fix was verified by running the backend server locally and testing the /images/toggle-favourite endpoint.

The endpoint now returns successful responses without raising errors.

Basic functionality of the favourite toggle feature was confirmed after the change.

Before

The /images/toggle-favourite endpoint returned an Internal Server Error due to failure when converting SQLite rows to dictionaries.

Example error:

dictionary update sequence element #0 has length 1; 2 is required

After

The database connection now uses:

conn.row_factory = sqlite3.Row

Rows are returned as dictionary-like objects, allowing safe conversion using dict(row).

The /images/toggle-favourite endpoint now works correctly and returns successful responses.

Notes

Please let me know if any changes or improvements are required. I would be happy to update the PR accordingly.

Closes #1200

Summary by CodeRabbit

  • New Features

    • Introduced new image retrieval functionality to efficiently fetch individual images by their unique identifier.
  • Chores

    • Enhanced database connection management with optimized configuration settings and improved query handling capabilities.
    • Simplified metadata processing logic to provide more reliable handling of default metadata values.
    • Significantly expanded automated test coverage for database connection behavior, configuration settings, and metadata storage operations.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 5, 2026

Walkthrough

Database connection infrastructure enhanced with PRAGMA configurations and row_factory settings. New image retrieval function added alongside simplified metadata parsing logic. Test infrastructure refactored with temporary database fixture and comprehensive test coverage for connection and metadata operations.

Changes

Cohort / File(s) Summary
Database Connection Setup
backend/.gitignore, backend/app/database/connection.py
Added formatting to .gitignore; enhanced connection initialization with PRAGMA statements (foreign_keys ON, recursive_triggers ON, etc.), sqlite3.Row factory, and explicit commit on context entry.
Database Image & Metadata Operations
backend/app/database/images.py, backend/app/utils/images.py
Added db_get_image_by_id() function for single image retrieval with parameterized queries; simplified image_util_parse_metadata() to return defaults for falsy input or pass through truthy input without JSON parsing.
Test Infrastructure
backend/tests/conftest.py
Replaced setup_before_all_tests() fixture with temp_db_path() fixture that creates isolated temporary SQLite databases and patches DATABASE_PATH across modules for test isolation.
Database Test Suite
backend/tests/database/test_connection.py, backend/tests/database/test_metadata.py
Added comprehensive tests covering connection context manager lifecycle (commit, rollback, closure, PRAGMA enforcement) and metadata operations (table creation, get, update with invalid JSON handling).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

Python

Poem

🐰 Databases now dance with pragma and care,
Connections committed with context so fair,
Tests in temp tunnels, all isolated and bright,
Images hop quick by their ID in the night,
Metadata parsing now simpler, more light!

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly addresses the main fix in the PR: setting SQLite row_factory to support dictionary-style access, which resolves the toggle-favourite endpoint error.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • 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

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 5, 2026

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

@Riddhi8077
Copy link
Copy Markdown
Author

@coderabbitai Review

@Riddhi8077 Riddhi8077 marked this pull request as ready for review March 5, 2026 11:41
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 5, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

1 similar comment
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 5, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 5, 2026

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

Copy link
Copy Markdown
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: 2

🧹 Nitpick comments (4)
backend/app/database/images.py (1)

424-444: Add return type hint.

The function lacks a return type annotation. Consider adding -> dict | None or -> Optional[dict] for better type safety.

♻️ Suggested fix
-def db_get_image_by_id(image_id: str):
+def db_get_image_by_id(image_id: str) -> dict | None:
     """
     Retrieve a single image by its ID.
     """
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/app/database/images.py` around lines 424 - 444, The function
db_get_image_by_id is missing a return type annotation; update its signature to
include a precise return type like -> dict | None (or -> Optional[dict] with
from typing import Optional) so callers and type checkers know it returns either
a dict or None; modify the def db_get_image_by_id(image_id: str): line to def
db_get_image_by_id(image_id: str) -> dict | None: (or use Optional[dict]) and
add any necessary import.
backend/tests/conftest.py (2)

7-13: Unused imports can be removed.

These table creation function imports (db_create_faces_table, db_create_images_table, etc.) appear to be remnants from the removed setup_before_all_tests() fixture and are no longer used in the current temp_db_path fixture.

♻️ Suggested cleanup
 import pytest
 import os
 import tempfile
 import sqlite3
-
-# Import database table creation functions
-from app.database.faces import db_create_faces_table
-from app.database.images import db_create_images_table
-from app.database.face_clusters import db_create_clusters_table
-from app.database.yolo_mapping import db_create_YOLO_classes_table
-from app.database.albums import db_create_albums_table, db_create_album_images_table
-from app.database.folders import db_create_folders_table
-from app.database.metadata import db_create_metadata_table
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/tests/conftest.py` around lines 7 - 13, Remove the unused
table-creation imports from conftest: delete the imports of
db_create_faces_table, db_create_images_table, db_create_clusters_table,
db_create_YOLO_classes_table, db_create_albums_table,
db_create_album_images_table, db_create_folders_table, and
db_create_metadata_table since they are no longer referenced by the temp_db_path
fixture or anywhere in the file; keep only the imports actually used by the
current fixtures/tests to clean up dead code.

48-48: Trailing whitespace.

Line 48 contains trailing whitespace that should be removed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/tests/conftest.py` at line 48, Open backend/tests/conftest.py and
remove the trailing whitespace from the blank line that currently contains only
whitespace (the empty line around the test fixtures setup) so that the file has
no extra spaces at the end of that line; this is a simple whitespace cleanup—no
code logic changes required.
backend/tests/database/test_connection.py (1)

1-4: Unused imports.

os and tempfile are imported but not used in this test file.

♻️ Suggested cleanup
-import os
 import sqlite3
-import tempfile
 import pytest
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/tests/database/test_connection.py` around lines 1 - 4, The file
imports unused modules os and tempfile which should be removed to clean up
imports; update the top of backend/tests/database/test_connection.py to only
import the actually used symbols (keep sqlite3 and pytest) and delete the unused
os and tempfile imports so there are no unused-import warnings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/app/database/images.py`:
- Line 21: The file imports get_db_connection twice causing a duplicate import;
remove the redundant import statement that references get_db_connection (from
app.database.connection) so only the original import at the earlier location
remains; search for the symbol get_db_connection in
backend/app/database/images.py and delete the duplicate import line while
keeping the existing import used by any functions or classes in that module.

In `@backend/app/utils/images.py`:
- Around line 496-508: The function image_util_parse_metadata currently returns
the raw truthy metadata (which may be a JSON string) instead of a dict; update
image_util_parse_metadata to detect if metadata is a str and json.loads it
(import json), handle json.JSONDecodeError by logging or falling back to the
default dict, and if metadata is already a dict return it; keep the existing
default-return behavior when metadata is falsy so callers like db_get_all_images
and Pydantic's MetadataModel receive a proper dict.

---

Nitpick comments:
In `@backend/app/database/images.py`:
- Around line 424-444: The function db_get_image_by_id is missing a return type
annotation; update its signature to include a precise return type like -> dict |
None (or -> Optional[dict] with from typing import Optional) so callers and type
checkers know it returns either a dict or None; modify the def
db_get_image_by_id(image_id: str): line to def db_get_image_by_id(image_id: str)
-> dict | None: (or use Optional[dict]) and add any necessary import.

In `@backend/tests/conftest.py`:
- Around line 7-13: Remove the unused table-creation imports from conftest:
delete the imports of db_create_faces_table, db_create_images_table,
db_create_clusters_table, db_create_YOLO_classes_table, db_create_albums_table,
db_create_album_images_table, db_create_folders_table, and
db_create_metadata_table since they are no longer referenced by the temp_db_path
fixture or anywhere in the file; keep only the imports actually used by the
current fixtures/tests to clean up dead code.
- Line 48: Open backend/tests/conftest.py and remove the trailing whitespace
from the blank line that currently contains only whitespace (the empty line
around the test fixtures setup) so that the file has no extra spaces at the end
of that line; this is a simple whitespace cleanup—no code logic changes
required.

In `@backend/tests/database/test_connection.py`:
- Around line 1-4: The file imports unused modules os and tempfile which should
be removed to clean up imports; update the top of
backend/tests/database/test_connection.py to only import the actually used
symbols (keep sqlite3 and pytest) and delete the unused os and tempfile imports
so there are no unused-import warnings.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 317c6bb4-fd50-4ac1-b601-56bada863c7f

📥 Commits

Reviewing files that changed from the base of the PR and between 8274ebd and b090754.

📒 Files selected for processing (7)
  • backend/.gitignore
  • backend/app/database/connection.py
  • backend/app/database/images.py
  • backend/app/utils/images.py
  • backend/tests/conftest.py
  • backend/tests/database/test_connection.py
  • backend/tests/database/test_metadata.py

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 5, 2026

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

Copy link
Copy Markdown
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.

♻️ Duplicate comments (2)
backend/app/database/images.py (1)

21-21: 🛠️ Refactor suggestion | 🟠 Major

Duplicate import.

get_db_connection is already imported at line 9. This has been flagged in a previous review.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/app/database/images.py` at line 21, The file contains a duplicate
import of get_db_connection; remove the redundant import line so only the single
import of get_db_connection remains (keep the original import at its first
occurrence and delete the later one referencing get_db_connection in images.py),
ensuring any references in functions like those in this module still use the
existing imported get_db_connection.
backend/app/utils/images.py (1)

496-508: ⚠️ Potential issue | 🔴 Critical

Critical: JSON parsing removed breaks existing functionality.

This has already been flagged in a previous review. The function now returns the raw metadata string instead of parsing the JSON. Callers like db_get_all_images (line 183) and the routes expect a dict, but with metadata stored as TEXT (JSON string) in SQLite, this will cause Pydantic validation failures.

The JSON parsing logic must be preserved.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/app/utils/images.py` around lines 496 - 508, The function
image_util_parse_metadata currently returns the raw metadata (likely a JSON
string) which breaks callers expecting a dict (e.g., db_get_all_images and
various routes); update image_util_parse_metadata to detect if metadata is a
string and parse it with json.loads, handle ValueError/JSONDecodeError by
logging or swallowing and returning the existing default dict (same shape as the
current default), and ensure the function always returns a dict; also add or
ensure the json import is present and preserve existing default keys
("name","date_created","width","height","file_location","file_size","item_type")
so Pydantic validation downstream continues to work.
🧹 Nitpick comments (5)
backend/tests/database/test_connection.py (1)

1-4: Remove unused imports.

os and tempfile are imported but not used in this file.

♻️ Proposed fix
-import os
 import sqlite3
-import tempfile
 import pytest
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/tests/database/test_connection.py` around lines 1 - 4, Remove the
unused imports in test_connection.py by deleting the import statements for os
and tempfile so only the actually used modules (sqlite3 and pytest) remain;
update the top-of-file import list to no longer reference the unused symbols
`os` and `tempfile` to satisfy linting and avoid unused-import warnings.
backend/tests/conftest.py (2)

3-4: Remove unused import.

sqlite3 is imported but not used in this file.

♻️ Proposed fix
 import tempfile
-import sqlite3
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/tests/conftest.py` around lines 3 - 4, Remove the unused top-level
import "sqlite3" from the file (delete the line importing sqlite3) so only
necessary imports remain (e.g., keep "import tempfile"); ensure no other
references to sqlite3 exist in functions or fixtures such as any conftest
fixtures before committing.

48-48: Minor: Trailing whitespace on line 48.

There's a stray space character before the empty line at the end of the file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/tests/conftest.py` at line 48, Remove the trailing whitespace on line
48 of backend/tests/conftest.py by deleting the stray space character at the end
of that line (ensure the file ends with a clean newline only); no code logic
changes required—just trim the extra space so the file ends with an empty line
without trailing whitespace.
backend/app/database/connection.py (1)

11-12: Consider improving the comment to explain the "why".

The comment "VERY IMPORTANT" doesn't explain why row_factory is critical. A brief explanation would help future maintainers understand its purpose.

💡 Suggested improvement
-    # VERY IMPORTANT
+    # Enable dict-like access to rows (required for dict(row) conversions)
     conn.row_factory = sqlite3.Row
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/app/database/connection.py` around lines 11 - 12, Replace the
ambiguous comment "VERY IMPORTANT" before setting conn.row_factory = sqlite3.Row
with a short explanatory comment that states why it's required (e.g., to make
query results sqlite3.Row objects so callers can access columns by name like
row["col"] or convert rows to dicts), and mention any downstream code that
depends on named-column access (reference conn.row_factory and sqlite3.Row) so
future maintainers understand the rationale.
backend/app/database/images.py (1)

424-444: Add return type hint for consistency with codebase patterns.

The function lacks a return type annotation. Adding it improves code clarity and IDE support.

💡 Suggested improvement
-def db_get_image_by_id(image_id: str):
+def db_get_image_by_id(image_id: str) -> dict | None:
     """
     Retrieve a single image by its ID.
     """

The function implementation correctly uses the context-managed connection with sqlite3.Row support, enabling dict(row) conversion.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/app/database/images.py` around lines 424 - 444, Add a return type
annotation to db_get_image_by_id to match project patterns (e.g., def
db_get_image_by_id(image_id: str) -> Optional[dict]): update imports to include
Optional (and Dict/Any if you prefer a more specific type) and ensure the
function still returns None or dict(row) as before; reference db_get_image_by_id
and get_db_connection to locate the function and adjust the typing imports at
the top of the file.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@backend/app/database/images.py`:
- Line 21: The file contains a duplicate import of get_db_connection; remove the
redundant import line so only the single import of get_db_connection remains
(keep the original import at its first occurrence and delete the later one
referencing get_db_connection in images.py), ensuring any references in
functions like those in this module still use the existing imported
get_db_connection.

In `@backend/app/utils/images.py`:
- Around line 496-508: The function image_util_parse_metadata currently returns
the raw metadata (likely a JSON string) which breaks callers expecting a dict
(e.g., db_get_all_images and various routes); update image_util_parse_metadata
to detect if metadata is a string and parse it with json.loads, handle
ValueError/JSONDecodeError by logging or swallowing and returning the existing
default dict (same shape as the current default), and ensure the function always
returns a dict; also add or ensure the json import is present and preserve
existing default keys
("name","date_created","width","height","file_location","file_size","item_type")
so Pydantic validation downstream continues to work.

---

Nitpick comments:
In `@backend/app/database/connection.py`:
- Around line 11-12: Replace the ambiguous comment "VERY IMPORTANT" before
setting conn.row_factory = sqlite3.Row with a short explanatory comment that
states why it's required (e.g., to make query results sqlite3.Row objects so
callers can access columns by name like row["col"] or convert rows to dicts),
and mention any downstream code that depends on named-column access (reference
conn.row_factory and sqlite3.Row) so future maintainers understand the
rationale.

In `@backend/app/database/images.py`:
- Around line 424-444: Add a return type annotation to db_get_image_by_id to
match project patterns (e.g., def db_get_image_by_id(image_id: str) ->
Optional[dict]): update imports to include Optional (and Dict/Any if you prefer
a more specific type) and ensure the function still returns None or dict(row) as
before; reference db_get_image_by_id and get_db_connection to locate the
function and adjust the typing imports at the top of the file.

In `@backend/tests/conftest.py`:
- Around line 3-4: Remove the unused top-level import "sqlite3" from the file
(delete the line importing sqlite3) so only necessary imports remain (e.g., keep
"import tempfile"); ensure no other references to sqlite3 exist in functions or
fixtures such as any conftest fixtures before committing.
- Line 48: Remove the trailing whitespace on line 48 of
backend/tests/conftest.py by deleting the stray space character at the end of
that line (ensure the file ends with a clean newline only); no code logic
changes required—just trim the extra space so the file ends with an empty line
without trailing whitespace.

In `@backend/tests/database/test_connection.py`:
- Around line 1-4: Remove the unused imports in test_connection.py by deleting
the import statements for os and tempfile so only the actually used modules
(sqlite3 and pytest) remain; update the top-of-file import list to no longer
reference the unused symbols `os` and `tempfile` to satisfy linting and avoid
unused-import warnings.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d91ad0b1-2dcd-40ec-894a-9b4409388f88

📥 Commits

Reviewing files that changed from the base of the PR and between 8274ebd and b090754.

📒 Files selected for processing (7)
  • backend/.gitignore
  • backend/app/database/connection.py
  • backend/app/database/images.py
  • backend/app/utils/images.py
  • backend/tests/conftest.py
  • backend/tests/database/test_connection.py
  • backend/tests/database/test_metadata.py

Removed unused import statement for database connection.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 5, 2026

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 5, 2026

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 5, 2026

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

@SIDDHANTCOOKIE
Copy link
Copy Markdown

Closing this as unreviewed issue

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.

Refactor/toggle-favourite endpoint to eliminate full-table query and improve query efficiency

2 participants