Skip to content

Commit 7dc1f83

Browse files
committed
fix: PostgreSQL read methods and delete_entity_relation bugs
Why this change is needed: After implementing model isolation, two critical bugs were discovered that would cause data access failures: Bug 1: In delete_entity_relation(), the SQL query uses positional parameters ($1, $2) but the parameter dict was not converted to a list of values before passing to db.execute(). This caused parameter binding failures when trying to delete entity relations. Bug 2: Four read methods (get_by_id, get_by_ids, get_vectors_by_ids, drop) were still using namespace_to_table_name(self.namespace) to get legacy table names instead of self.table_name with model suffix. This meant these methods would query the wrong table (legacy without suffix) while data was being inserted into the new table (with suffix), causing data not found errors. How it solves it: - Bug 1: Convert parameter dict to list using list(params.values()) before passing to db.execute(), matching the pattern used in other methods - Bug 2: Replace all namespace_to_table_name(self.namespace) calls with self.table_name in the four affected methods, ensuring they query the correct model-specific table Impact: - delete_entity_relation now correctly deletes relations by entity name - All read operations now correctly query model-specific tables - Data written with model isolation can now be properly retrieved - Maintains consistency with write operations using self.table_name Testing: - All 6 PostgreSQL migration tests pass (test_postgres_migration.py) - All 6 Qdrant migration tests pass (test_qdrant_migration.py) - Verified parameter binding works correctly - Verified read methods access correct tables
1 parent ad68624 commit 7dc1f83

File tree

1 file changed

+6
-35
lines changed

1 file changed

+6
-35
lines changed

lightrag/kg/postgres_impl.py

Lines changed: 6 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2604,9 +2604,8 @@ async def delete_entity_relation(self, entity_name: str) -> None:
26042604
delete_sql = f"""DELETE FROM {self.table_name}
26052605
WHERE workspace=$1 AND (source_id=$2 OR target_id=$2)"""
26062606

2607-
await self.db.execute(
2608-
delete_sql, {"workspace": self.workspace, "entity_name": entity_name}
2609-
)
2607+
params = {"workspace": self.workspace, "entity_name": entity_name}
2608+
await self.db.execute(delete_sql, list(params.values()))
26102609
logger.debug(
26112610
f"[{self.workspace}] Successfully deleted relations for entity {entity_name}"
26122611
)
@@ -2624,14 +2623,7 @@ async def get_by_id(self, id: str) -> dict[str, Any] | None:
26242623
Returns:
26252624
The vector data if found, or None if not found
26262625
"""
2627-
table_name = namespace_to_table_name(self.namespace)
2628-
if not table_name:
2629-
logger.error(
2630-
f"[{self.workspace}] Unknown namespace for ID lookup: {self.namespace}"
2631-
)
2632-
return None
2633-
2634-
query = f"SELECT *, EXTRACT(EPOCH FROM create_time)::BIGINT as created_at FROM {table_name} WHERE workspace=$1 AND id=$2"
2626+
query = f"SELECT *, EXTRACT(EPOCH FROM create_time)::BIGINT as created_at FROM {self.table_name} WHERE workspace=$1 AND id=$2"
26352627
params = {"workspace": self.workspace, "id": id}
26362628

26372629
try:
@@ -2657,15 +2649,8 @@ async def get_by_ids(self, ids: list[str]) -> list[dict[str, Any]]:
26572649
if not ids:
26582650
return []
26592651

2660-
table_name = namespace_to_table_name(self.namespace)
2661-
if not table_name:
2662-
logger.error(
2663-
f"[{self.workspace}] Unknown namespace for IDs lookup: {self.namespace}"
2664-
)
2665-
return []
2666-
26672652
ids_str = ",".join([f"'{id}'" for id in ids])
2668-
query = f"SELECT *, EXTRACT(EPOCH FROM create_time)::BIGINT as created_at FROM {table_name} WHERE workspace=$1 AND id IN ({ids_str})"
2653+
query = f"SELECT *, EXTRACT(EPOCH FROM create_time)::BIGINT as created_at FROM {self.table_name} WHERE workspace=$1 AND id IN ({ids_str})"
26692654
params = {"workspace": self.workspace}
26702655

26712656
try:
@@ -2706,15 +2691,8 @@ async def get_vectors_by_ids(self, ids: list[str]) -> dict[str, list[float]]:
27062691
if not ids:
27072692
return {}
27082693

2709-
table_name = namespace_to_table_name(self.namespace)
2710-
if not table_name:
2711-
logger.error(
2712-
f"[{self.workspace}] Unknown namespace for vector lookup: {self.namespace}"
2713-
)
2714-
return {}
2715-
27162694
ids_str = ",".join([f"'{id}'" for id in ids])
2717-
query = f"SELECT id, content_vector FROM {table_name} WHERE workspace=$1 AND id IN ({ids_str})"
2695+
query = f"SELECT id, content_vector FROM {self.table_name} WHERE workspace=$1 AND id IN ({ids_str})"
27182696
params = {"workspace": self.workspace}
27192697

27202698
try:
@@ -2743,15 +2721,8 @@ async def get_vectors_by_ids(self, ids: list[str]) -> dict[str, list[float]]:
27432721
async def drop(self) -> dict[str, str]:
27442722
"""Drop the storage"""
27452723
try:
2746-
table_name = namespace_to_table_name(self.namespace)
2747-
if not table_name:
2748-
return {
2749-
"status": "error",
2750-
"message": f"Unknown namespace: {self.namespace}",
2751-
}
2752-
27532724
drop_sql = SQL_TEMPLATES["drop_specifiy_table_workspace"].format(
2754-
table_name=table_name
2725+
table_name=self.table_name
27552726
)
27562727
await self.db.execute(drop_sql, {"workspace": self.workspace})
27572728
return {"status": "success", "message": "data dropped"}

0 commit comments

Comments
 (0)