From 7d7e7c13a72ea2e8af763276b50e3482be7310c6 Mon Sep 17 00:00:00 2001 From: Alex Blanck Date: Wed, 21 Aug 2024 17:02:29 -0700 Subject: [PATCH] Fix sqlalchemy error when saving synced issues Reviewed By: yuhshin-oss Differential Revision: D61560578 fbshipit-source-id: 9e2987eb7376ca718cebb1f9c6a0964f46ec0185 --- sapp/bulk_saver.py | 21 ++++++++++++++++++++- sapp/pipeline/model_generator.py | 2 ++ sapp/tests/fake_object_generator.py | 2 ++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/sapp/bulk_saver.py b/sapp/bulk_saver.py index 0f8fdfc..b883648 100644 --- a/sapp/bulk_saver.py +++ b/sapp/bulk_saver.py @@ -9,10 +9,11 @@ """ import logging -from typing import Any, Callable, Dict, List, Optional, Type +from typing import Any, Dict, List, Optional, Type from sqlalchemy.dialects.mysql import insert as mysql_insert from sqlalchemy.dialects.sqlite import insert as sqlite_insert +from tools.sapp.sapp.models import Base from .db import DB from .decorators import log_time @@ -191,6 +192,12 @@ def _save_batch_and_handle_key_conflicts( {k: v for k, v in cls.to_dict(r).items() if k != "model"} for r in batch ] + # Simulate the behavior of bulk_insert_mappings render_nulls + # to avoid errors when a column is missing from some but not all of the + # records which can occur when some issues are synced from central issues + # and others are created from scratch + records_to_save = self._render_nulls(cls, records_to_save) + dialect = database.engine.dialect.name if dialect == "mysql": statement = ( @@ -234,6 +241,18 @@ def _save_batch_and_handle_key_conflicts( f"records." ) + def _render_nulls( + self, + cls: Type[Base], + records: List[Dict[str, Any]], + ) -> List[Dict[str, Any]]: + column_keys = cls.__table__.columns.keys() + for record in records: + for key in column_keys: + if key not in record: + record[key] = None + return records + # Save a batch of records to the database, failing on duplicate key errors. # # This is more efficient than `_save_batch_and_handle_key_conflicts` for records diff --git a/sapp/pipeline/model_generator.py b/sapp/pipeline/model_generator.py index 82d170f..d28aa3b 100644 --- a/sapp/pipeline/model_generator.py +++ b/sapp/pipeline/model_generator.py @@ -204,6 +204,8 @@ def _generate_issue( status=IssueStatus.UNCATEGORIZED, detected_time=int(run.date.timestamp()), first_instance_id=instance_id, + update_time=0, + triage_duration=0, ) self.graph.add_issue(issue) diff --git a/sapp/tests/fake_object_generator.py b/sapp/tests/fake_object_generator.py index 7bd1958..96b8f60 100644 --- a/sapp/tests/fake_object_generator.py +++ b/sapp/tests/fake_object_generator.py @@ -72,6 +72,8 @@ def issue( status=status, detected_time=int(now.timestamp()), first_instance_id=DBID(10072), + update_time=0, + triage_duration=0, ) if self.graph: # pyre-fixme[6]: For 1st param expected `Issue` but got `Munch`.