/****************************************************************************** * @file evidence_entity_dao.c * @brief Persistance des associations entre preuves et entités. ******************************************************************************/ #include "dao/evidence_entity_dao.h" #include "database/error.h" #include "database/statement.h" #include /** * @brief Représentation privée du DAO. * * La connexion Database est empruntée. */ struct EvidenceEntityDao { Database *database; }; /** * @brief Requête vérifiant l'existence d'une preuve. */ static const char *const evidence_entity_dao_evidence_exists_sql = "SELECT 1 " "FROM preuves " "WHERE id = ? " "LIMIT 1;"; /** * @brief Requête vérifiant l'existence d'une entité. */ static const char *const evidence_entity_dao_entity_exists_sql = "SELECT 1 " "FROM entites " "WHERE id = ? " "LIMIT 1;"; /** * @brief Requête vérifiant l'existence d'une association. */ static const char *const evidence_entity_dao_association_exists_sql = "SELECT 1 " "FROM preuve_entites " "WHERE preuve_id = ? " "AND entite_id = ? " "LIMIT 1;"; /** * @brief Requête créant une association. */ static const char *const evidence_entity_dao_link_sql = "INSERT INTO preuve_entites" "(" " preuve_id," " entite_id" ")" "VALUES" "(" " ?," " ?" ");"; /** * @brief Requête supprimant une association. */ static const char *const evidence_entity_dao_unlink_sql = "DELETE FROM preuve_entites " "WHERE preuve_id = ? " "AND entite_id = ?;"; /** * @brief Requête listant les entités liées à une preuve. */ static const char *const evidence_entity_dao_list_entity_identifiers_sql = "SELECT entite_id " "FROM preuve_entites " "WHERE preuve_id = ? " "ORDER BY entite_id ASC;"; /** * @brief Requête listant les preuves liées à une entité. */ static const char *const evidence_entity_dao_list_evidence_identifiers_sql = "SELECT preuve_id " "FROM preuve_entites " "WHERE entite_id = ? " "ORDER BY preuve_id ASC;"; /** * @brief Enregistre une erreur littérale du DAO. */ static void evidence_entity_dao_set_error_literal( GError **error, EvidenceEntityDaoError error_code, const char *message ) { if (error == NULL || *error != NULL) { return; } g_set_error_literal( error, EVIDENCE_ENTITY_DAO_ERROR, error_code, message ); } /** * @brief Détermine si SQLite signale un schéma incompatible. */ static gboolean evidence_entity_dao_is_schema_error( const char *database_message ) { if (database_message == NULL) { return FALSE; } return g_strstr_len( database_message, -1, "no such table" ) != NULL || g_strstr_len( database_message, -1, "no such column" ) != NULL; } /** * @brief Détermine si SQLite signale une contrainte. */ static gboolean evidence_entity_dao_is_constraint_error( const char *database_message ) { if (database_message == NULL) { return FALSE; } return g_strstr_len( database_message, -1, "constraint" ) != NULL || g_strstr_len( database_message, -1, "CONSTRAINT" ) != NULL; } /** * @brief Transforme la dernière erreur Database en erreur du DAO. */ static void evidence_entity_dao_set_database_error( EvidenceEntityDao *evidence_entity_dao, GError **error, EvidenceEntityDaoError error_code, const char *context ) { const char *database_message = NULL; if (error == NULL || *error != NULL) { return; } if (evidence_entity_dao != NULL && evidence_entity_dao->database != NULL) { database_message = database_error_get_message( evidence_entity_dao->database ); } if (error_code == EVIDENCE_ENTITY_DAO_ERROR_PREPARE && evidence_entity_dao_is_schema_error( database_message )) { error_code = EVIDENCE_ENTITY_DAO_ERROR_SCHEMA; } if (database_message != NULL && database_message[0] != '\0') { g_set_error( error, EVIDENCE_ENTITY_DAO_ERROR, error_code, "%s : %s", context, database_message ); return; } g_set_error_literal( error, EVIDENCE_ENTITY_DAO_ERROR, error_code, context ); } /** * @brief Vérifie le DAO et les deux UUID d'une association. */ static gboolean evidence_entity_dao_validate_association( EvidenceEntityDao *evidence_entity_dao, const char *evidence_identifier, const char *entity_identifier, GError **error ) { if (evidence_entity_dao == NULL || evidence_entity_dao->database == NULL) { evidence_entity_dao_set_error_literal( error, EVIDENCE_ENTITY_DAO_ERROR_INVALID_ARGUMENT, "Le DAO des associations preuve-entité est invalide." ); return FALSE; } if (evidence_identifier == NULL || !g_uuid_string_is_valid( evidence_identifier )) { evidence_entity_dao_set_error_literal( error, EVIDENCE_ENTITY_DAO_ERROR_INVALID_ARGUMENT, "L'identifiant de preuve est invalide." ); return FALSE; } if (entity_identifier == NULL || !g_uuid_string_is_valid( entity_identifier )) { evidence_entity_dao_set_error_literal( error, EVIDENCE_ENTITY_DAO_ERROR_INVALID_ARGUMENT, "L'identifiant d'entité est invalide." ); return FALSE; } return TRUE; } /** * @brief Vérifie le DAO et un UUID utilisé pour une liste. */ static gboolean evidence_entity_dao_validate_list_request( EvidenceEntityDao *evidence_entity_dao, const char *identifier, const char *invalid_identifier_message, GError **error ) { if (evidence_entity_dao == NULL || evidence_entity_dao->database == NULL) { evidence_entity_dao_set_error_literal( error, EVIDENCE_ENTITY_DAO_ERROR_INVALID_ARGUMENT, "Le DAO des associations preuve-entité est invalide." ); return FALSE; } if (identifier == NULL || !g_uuid_string_is_valid( identifier )) { evidence_entity_dao_set_error_literal( error, EVIDENCE_ENTITY_DAO_ERROR_INVALID_ARGUMENT, invalid_identifier_message ); return FALSE; } return TRUE; } /** * @brief Exécute une requête SELECT 1 avec un ou deux paramètres. */ static gboolean evidence_entity_dao_query_exists( EvidenceEntityDao *evidence_entity_dao, const char *sql, const char *first_value, const char *second_value, gboolean *out_exists, GError **error ) { DatabaseStatement *statement = NULL; DatabaseStatementStepResult step_result = DATABASE_STATEMENT_STEP_ERROR; gboolean success = FALSE; if (evidence_entity_dao == NULL || evidence_entity_dao->database == NULL || sql == NULL || first_value == NULL || out_exists == NULL) { evidence_entity_dao_set_error_literal( error, EVIDENCE_ENTITY_DAO_ERROR_INVALID_ARGUMENT, "Les paramètres de la recherche d'existence sont invalides." ); return FALSE; } *out_exists = FALSE; statement = database_statement_prepare( evidence_entity_dao->database, sql ); if (statement == NULL) { evidence_entity_dao_set_database_error( evidence_entity_dao, error, EVIDENCE_ENTITY_DAO_ERROR_PREPARE, "Impossible de préparer la recherche d'existence" ); goto cleanup; } if (!database_statement_bind_text( statement, 1, first_value )) { evidence_entity_dao_set_database_error( evidence_entity_dao, error, EVIDENCE_ENTITY_DAO_ERROR_BIND, "Impossible de lier le premier identifiant" ); goto cleanup; } if (second_value != NULL && !database_statement_bind_text( statement, 2, second_value )) { evidence_entity_dao_set_database_error( evidence_entity_dao, error, EVIDENCE_ENTITY_DAO_ERROR_BIND, "Impossible de lier le second identifiant" ); goto cleanup; } step_result = database_statement_step( statement ); if (step_result == DATABASE_STATEMENT_STEP_ERROR) { evidence_entity_dao_set_database_error( evidence_entity_dao, error, EVIDENCE_ENTITY_DAO_ERROR_EXECUTE, "Impossible d'exécuter la recherche d'existence" ); goto cleanup; } *out_exists = step_result == DATABASE_STATEMENT_STEP_ROW; success = TRUE; cleanup: database_statement_finalize( statement ); return success; } /** * @brief Exécute une modification à deux UUID. */ static gboolean evidence_entity_dao_execute_pair_statement( EvidenceEntityDao *evidence_entity_dao, const char *sql, const char *evidence_identifier, const char *entity_identifier, const char *prepare_context, const char *bind_context, const char *execute_context, GError **error ) { DatabaseStatement *statement = NULL; DatabaseStatementStepResult step_result = DATABASE_STATEMENT_STEP_ERROR; const char *database_message = NULL; gboolean success = FALSE; statement = database_statement_prepare( evidence_entity_dao->database, sql ); if (statement == NULL) { evidence_entity_dao_set_database_error( evidence_entity_dao, error, EVIDENCE_ENTITY_DAO_ERROR_PREPARE, prepare_context ); goto cleanup; } if (!database_statement_bind_text( statement, 1, evidence_identifier ) || !database_statement_bind_text( statement, 2, entity_identifier )) { evidence_entity_dao_set_database_error( evidence_entity_dao, error, EVIDENCE_ENTITY_DAO_ERROR_BIND, bind_context ); goto cleanup; } step_result = database_statement_step( statement ); if (step_result != DATABASE_STATEMENT_STEP_DONE) { database_message = database_error_get_message( evidence_entity_dao->database ); if (evidence_entity_dao_is_constraint_error( database_message )) { evidence_entity_dao_set_database_error( evidence_entity_dao, error, EVIDENCE_ENTITY_DAO_ERROR_CONSTRAINT, execute_context ); } else { evidence_entity_dao_set_database_error( evidence_entity_dao, error, EVIDENCE_ENTITY_DAO_ERROR_EXECUTE, execute_context ); } goto cleanup; } success = TRUE; cleanup: database_statement_finalize( statement ); return success; } /** * @brief Charge une liste d'UUID depuis une requête à un paramètre. */ static GPtrArray *evidence_entity_dao_list_identifiers( EvidenceEntityDao *evidence_entity_dao, const char *sql, const char *identifier, const char *prepare_context, const char *bind_context, const char *execute_context, GError **error ) { DatabaseStatement *statement = NULL; GPtrArray *identifiers = NULL; DatabaseStatementStepResult step_result = DATABASE_STATEMENT_STEP_ERROR; identifiers = g_ptr_array_new_with_free_func( g_free ); if (identifiers == NULL) { evidence_entity_dao_set_error_literal( error, EVIDENCE_ENTITY_DAO_ERROR_MEMORY, "Impossible d'allouer la liste d'identifiants." ); return NULL; } statement = database_statement_prepare( evidence_entity_dao->database, sql ); if (statement == NULL) { evidence_entity_dao_set_database_error( evidence_entity_dao, error, EVIDENCE_ENTITY_DAO_ERROR_PREPARE, prepare_context ); goto failure; } if (!database_statement_bind_text( statement, 1, identifier )) { evidence_entity_dao_set_database_error( evidence_entity_dao, error, EVIDENCE_ENTITY_DAO_ERROR_BIND, bind_context ); goto failure; } while (TRUE) { char *loaded_identifier = NULL; step_result = database_statement_step( statement ); if (step_result == DATABASE_STATEMENT_STEP_DONE) { break; } if (step_result == DATABASE_STATEMENT_STEP_ERROR) { evidence_entity_dao_set_database_error( evidence_entity_dao, error, EVIDENCE_ENTITY_DAO_ERROR_EXECUTE, execute_context ); goto failure; } if (!database_statement_column_text( statement, 0, &loaded_identifier )) { evidence_entity_dao_set_database_error( evidence_entity_dao, error, EVIDENCE_ENTITY_DAO_ERROR_READ, "Impossible de lire un identifiant associé" ); g_free( loaded_identifier ); goto failure; } if (loaded_identifier == NULL || !g_uuid_string_is_valid( loaded_identifier )) { evidence_entity_dao_set_error_literal( error, EVIDENCE_ENTITY_DAO_ERROR_READ, "La base contient un identifiant associé invalide." ); g_free( loaded_identifier ); goto failure; } g_ptr_array_add( identifiers, loaded_identifier ); } database_statement_finalize( statement ); return identifiers; failure: database_statement_finalize( statement ); g_ptr_array_unref( identifiers ); return NULL; } GQuark evidence_entity_dao_error_quark(void) { return g_quark_from_static_string( "evidence-entity-dao-error-quark" ); } EvidenceEntityDao *evidence_entity_dao_new( Database *database, GError **error ) { EvidenceEntityDao *evidence_entity_dao = NULL; g_return_val_if_fail( error == NULL || *error == NULL, NULL ); if (database == NULL) { evidence_entity_dao_set_error_literal( error, EVIDENCE_ENTITY_DAO_ERROR_INVALID_ARGUMENT, "La connexion Database est obligatoire." ); return NULL; } evidence_entity_dao = g_try_new0( EvidenceEntityDao, 1 ); if (evidence_entity_dao == NULL) { evidence_entity_dao_set_error_literal( error, EVIDENCE_ENTITY_DAO_ERROR_MEMORY, "Impossible d'allouer le DAO des associations preuve-entité." ); return NULL; } evidence_entity_dao->database = database; return evidence_entity_dao; } void evidence_entity_dao_free( EvidenceEntityDao *evidence_entity_dao ) { if (evidence_entity_dao == NULL) { return; } evidence_entity_dao->database = NULL; g_free( evidence_entity_dao ); } gboolean evidence_entity_dao_link( EvidenceEntityDao *evidence_entity_dao, const char *evidence_identifier, const char *entity_identifier, GError **error ) { gboolean evidence_exists = FALSE; gboolean entity_exists = FALSE; gboolean association_exists = FALSE; g_return_val_if_fail( error == NULL || *error == NULL, FALSE ); if (!evidence_entity_dao_validate_association( evidence_entity_dao, evidence_identifier, entity_identifier, error )) { return FALSE; } if (!evidence_entity_dao_query_exists( evidence_entity_dao, evidence_entity_dao_evidence_exists_sql, evidence_identifier, NULL, &evidence_exists, error )) { return FALSE; } if (!evidence_exists) { evidence_entity_dao_set_error_literal( error, EVIDENCE_ENTITY_DAO_ERROR_NOT_FOUND, "La preuve à associer n'existe pas." ); return FALSE; } if (!evidence_entity_dao_query_exists( evidence_entity_dao, evidence_entity_dao_entity_exists_sql, entity_identifier, NULL, &entity_exists, error )) { return FALSE; } if (!entity_exists) { evidence_entity_dao_set_error_literal( error, EVIDENCE_ENTITY_DAO_ERROR_NOT_FOUND, "L'entité à associer n'existe pas." ); return FALSE; } if (!evidence_entity_dao_query_exists( evidence_entity_dao, evidence_entity_dao_association_exists_sql, evidence_identifier, entity_identifier, &association_exists, error )) { return FALSE; } if (association_exists) { evidence_entity_dao_set_error_literal( error, EVIDENCE_ENTITY_DAO_ERROR_CONSTRAINT, "Cette preuve est déjà associée à cette entité." ); return FALSE; } return evidence_entity_dao_execute_pair_statement( evidence_entity_dao, evidence_entity_dao_link_sql, evidence_identifier, entity_identifier, "Impossible de préparer la création de l'association", "Impossible de lier les identifiants de l'association", "Impossible de créer l'association", error ); } gboolean evidence_entity_dao_unlink( EvidenceEntityDao *evidence_entity_dao, const char *evidence_identifier, const char *entity_identifier, GError **error ) { gboolean association_exists = FALSE; g_return_val_if_fail( error == NULL || *error == NULL, FALSE ); if (!evidence_entity_dao_validate_association( evidence_entity_dao, evidence_identifier, entity_identifier, error )) { return FALSE; } if (!evidence_entity_dao_query_exists( evidence_entity_dao, evidence_entity_dao_association_exists_sql, evidence_identifier, entity_identifier, &association_exists, error )) { return FALSE; } if (!association_exists) { evidence_entity_dao_set_error_literal( error, EVIDENCE_ENTITY_DAO_ERROR_NOT_FOUND, "L'association entre la preuve et l'entité n'existe pas." ); return FALSE; } return evidence_entity_dao_execute_pair_statement( evidence_entity_dao, evidence_entity_dao_unlink_sql, evidence_identifier, entity_identifier, "Impossible de préparer la suppression de l'association", "Impossible de lier les identifiants de l'association", "Impossible de supprimer l'association", error ); } gboolean evidence_entity_dao_exists( EvidenceEntityDao *evidence_entity_dao, const char *evidence_identifier, const char *entity_identifier, gboolean *out_exists, GError **error ) { g_return_val_if_fail( error == NULL || *error == NULL, FALSE ); if (out_exists == NULL) { evidence_entity_dao_set_error_literal( error, EVIDENCE_ENTITY_DAO_ERROR_INVALID_ARGUMENT, "La destination du résultat d'existence est obligatoire." ); return FALSE; } if (!evidence_entity_dao_validate_association( evidence_entity_dao, evidence_identifier, entity_identifier, error )) { return FALSE; } return evidence_entity_dao_query_exists( evidence_entity_dao, evidence_entity_dao_association_exists_sql, evidence_identifier, entity_identifier, out_exists, error ); } GPtrArray *evidence_entity_dao_list_entity_identifiers( EvidenceEntityDao *evidence_entity_dao, const char *evidence_identifier, GError **error ) { g_return_val_if_fail( error == NULL || *error == NULL, NULL ); if (!evidence_entity_dao_validate_list_request( evidence_entity_dao, evidence_identifier, "L'identifiant de preuve est invalide.", error )) { return NULL; } return evidence_entity_dao_list_identifiers( evidence_entity_dao, evidence_entity_dao_list_entity_identifiers_sql, evidence_identifier, "Impossible de préparer la liste des entités associées", "Impossible de lier l'identifiant de preuve", "Impossible de charger les entités associées", error ); } GPtrArray *evidence_entity_dao_list_evidence_identifiers( EvidenceEntityDao *evidence_entity_dao, const char *entity_identifier, GError **error ) { g_return_val_if_fail( error == NULL || *error == NULL, NULL ); if (!evidence_entity_dao_validate_list_request( evidence_entity_dao, entity_identifier, "L'identifiant d'entité est invalide.", error )) { return NULL; } return evidence_entity_dao_list_identifiers( evidence_entity_dao, evidence_entity_dao_list_evidence_identifiers_sql, entity_identifier, "Impossible de préparer la liste des preuves associées", "Impossible de lier l'identifiant d'entité", "Impossible de charger les preuves associées", error ); } gboolean evidence_entity_dao_add_observation(EvidenceEntityDao *dao, const char *evidence_identifier, const char *entity_type, const char *value_raw, const char *value_normalized, const char *role, const char *provenance_kind, const char *source_header, guint occurrence, const char *verification_status, const char *created_at, char **out_observation_identifier, GError **error) { static const char *sql = "INSERT OR IGNORE INTO evidence_entity_observations(" "id,evidence_id,entity_type,value_raw,value_normalized,role," "provenance_kind,source_header,occurrence,verification_status," "observed_at,integrated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?);"; static const char *find_sql = "SELECT id FROM evidence_entity_observations WHERE evidence_id=? " "AND entity_type=? AND value_normalized=? AND role=? " "AND source_header=? AND occurrence=? AND provenance_kind=? " "AND extraction_id IS NULL LIMIT 1;"; DatabaseStatement *statement = NULL; char *new_identifier = NULL; gboolean success = FALSE; g_return_val_if_fail(error == NULL || *error == NULL, FALSE); if (out_observation_identifier != NULL) *out_observation_identifier = NULL; if (!evidence_entity_dao_validate_list_request(dao, evidence_identifier, "L'identifiant de preuve est invalide.", error) || entity_type == NULL || value_raw == NULL || value_normalized == NULL || role == NULL || source_header == NULL || occurrence == 0 || created_at == NULL) return FALSE; new_identifier = g_uuid_string_random(); statement = database_statement_prepare(dao->database, sql); if (statement == NULL) goto cleanup; success = database_statement_bind_text(statement, 1, new_identifier) && database_statement_bind_text(statement, 2, evidence_identifier) && database_statement_bind_text(statement, 3, entity_type) && database_statement_bind_text(statement, 4, value_raw) && database_statement_bind_text(statement, 5, value_normalized) && database_statement_bind_text(statement, 6, role) && database_statement_bind_text(statement, 7, provenance_kind) && database_statement_bind_text(statement, 8, source_header) && database_statement_bind_int64(statement, 9, occurrence) && database_statement_bind_text(statement, 10, verification_status) && database_statement_bind_text(statement, 11, created_at) && database_statement_bind_text(statement, 12, created_at) && database_statement_step(statement) == DATABASE_STATEMENT_STEP_DONE; database_statement_finalize(statement); statement = NULL; if (!success) goto cleanup; statement = database_statement_prepare(dao->database, find_sql); success = statement != NULL && database_statement_bind_text(statement, 1, evidence_identifier) && database_statement_bind_text(statement, 2, entity_type) && database_statement_bind_text(statement, 3, value_normalized) && database_statement_bind_text(statement, 4, role) && database_statement_bind_text(statement, 5, source_header) && database_statement_bind_int64(statement, 6, occurrence) && database_statement_bind_text(statement, 7, provenance_kind) && database_statement_step(statement) == DATABASE_STATEMENT_STEP_ROW && database_statement_column_text(statement, 0, out_observation_identifier); cleanup: if (!success) evidence_entity_dao_set_database_error(dao, error, EVIDENCE_ENTITY_DAO_ERROR_EXECUTE, "Impossible d’enregistrer l’observation preuve-entité"); database_statement_finalize(statement); g_free(new_identifier); return success; } gboolean evidence_entity_dao_promote_observation(EvidenceEntityDao *dao, const char *observation_identifier, const char *entity_identifier, const char *promoted_at, const char *promotion_kind, GError **error) { static const char *sql = "UPDATE evidence_entity_observations SET entity_id=?,promoted_at=?," "promotion_kind=? WHERE id=? AND (entity_id IS NULL OR entity_id=?);"; DatabaseStatement *statement = NULL; gboolean success = FALSE; g_return_val_if_fail(error == NULL || *error == NULL, FALSE); if (dao == NULL || !g_uuid_string_is_valid(observation_identifier) || !g_uuid_string_is_valid(entity_identifier) || promoted_at == NULL || (g_strcmp0(promotion_kind, "created") != 0 && g_strcmp0(promotion_kind, "reused") != 0)) return FALSE; statement = database_statement_prepare(dao->database, sql); success = statement != NULL && database_statement_bind_text(statement, 1, entity_identifier) && database_statement_bind_text(statement, 2, promoted_at) && database_statement_bind_text(statement, 3, promotion_kind) && database_statement_bind_text(statement, 4, observation_identifier) && database_statement_bind_text(statement, 5, entity_identifier) && database_statement_step(statement) == DATABASE_STATEMENT_STEP_DONE; if (!success) evidence_entity_dao_set_database_error(dao, error, EVIDENCE_ENTITY_DAO_ERROR_EXECUTE, "Impossible de promouvoir l’observation"); database_statement_finalize(statement); return success; } char *evidence_entity_dao_format_observations(EvidenceEntityDao *dao, const char *evidence_identifier, GError **error) { static const char *sql = "SELECT COALESCE(o.value_normalized,o.value_raw),o.entity_type,o.role," "o.source_header,o.occurrence,o.provenance_kind,o.verification_status," "o.integrated_at,o.entity_id,o.promotion_kind " "FROM evidence_entity_observations o WHERE o.evidence_id=? " "ORDER BY o.source_header,o.occurrence,1,o.role;"; DatabaseStatement *statement = NULL; GString *text = NULL; g_return_val_if_fail(error == NULL || *error == NULL, NULL); if (!evidence_entity_dao_validate_list_request(dao, evidence_identifier, "L'identifiant de preuve est invalide.", error)) return NULL; statement = database_statement_prepare(dao->database, sql); if (statement == NULL || !database_statement_bind_text(statement, 1, evidence_identifier)) goto failure; text = g_string_new(NULL); for (;;) { DatabaseStatementStepResult step = database_statement_step(statement); if (step == DATABASE_STATEMENT_STEP_DONE) break; if (step != DATABASE_STATEMENT_STEP_ROW) goto failure; char *value = NULL, *type = NULL, *role = NULL, *header = NULL; char *provenance = NULL, *status = NULL, *date = NULL; char *entity = NULL, *promotion = NULL; int64_t occurrence = 0; if (!database_statement_column_text(statement, 0, &value) || !database_statement_column_text(statement, 1, &type) || !database_statement_column_text(statement, 2, &role) || !database_statement_column_text(statement, 3, &header) || !database_statement_column_int64(statement, 4, &occurrence) || !database_statement_column_text(statement, 5, &provenance) || !database_statement_column_text(statement, 6, &status) || !database_statement_column_text(statement, 7, &date) || !database_statement_column_text(statement, 8, &entity) || !database_statement_column_text(statement, 9, &promotion)) { g_free(value); g_free(type); g_free(role); g_free(header); g_free(provenance); g_free(status); g_free(date); g_free(entity); g_free(promotion); goto failure; } g_string_append_printf(text, "%s%s — %s — rôle : %s — origine : %s" " #%lld — provenance : %s — validation : %s — date : %s — " "graphe : %s%s%s", text->len > 0 ? "\n" : "", value, type, role, header, (long long) occurrence, provenance, status, date, entity != NULL ? "promue" : "non ajoutée", promotion != NULL ? " (" : "", promotion != NULL ? promotion : ""); if (promotion != NULL) g_string_append_c(text, ')'); g_free(value); g_free(type); g_free(role); g_free(header); g_free(provenance); g_free(status); g_free(date); g_free(entity); g_free(promotion); } database_statement_finalize(statement); return g_string_free(text, FALSE); failure: evidence_entity_dao_set_database_error(dao, error, EVIDENCE_ENTITY_DAO_ERROR_EXECUTE, "Impossible de charger les observations preuve-entité"); database_statement_finalize(statement); if (text != NULL) g_string_free(text, TRUE); return NULL; } GPtrArray *evidence_entity_dao_list_observations(EvidenceEntityDao *dao, const char *evidence_identifier, GError **error) { static const char *sql = "SELECT id,COALESCE(value_normalized,value_raw),entity_type,role," "source_header,occurrence,provenance_kind,verification_status," "integrated_at,entity_id,promotion_kind " "FROM evidence_entity_observations WHERE evidence_id=? " "ORDER BY source_header,occurrence,2,role;"; DatabaseStatement *statement = NULL; GPtrArray *items = NULL; g_return_val_if_fail(error == NULL || *error == NULL, NULL); if (!evidence_entity_dao_validate_list_request(dao, evidence_identifier, "L'identifiant de preuve est invalide.", error)) return NULL; statement = database_statement_prepare(dao->database, sql); if (statement == NULL || !database_statement_bind_text(statement, 1, evidence_identifier)) goto failure; items = g_ptr_array_new_with_free_func( (GDestroyNotify) evidence_observation_free); for (;;) { DatabaseStatementStepResult step = database_statement_step(statement); if (step == DATABASE_STATEMENT_STEP_DONE) break; if (step != DATABASE_STATEMENT_STEP_ROW) goto failure; EvidenceObservation *item = g_new0(EvidenceObservation, 1); int64_t occurrence = 0; if (!database_statement_column_text(statement, 0, &item->identifier) || !database_statement_column_text(statement, 1, &item->value) || !database_statement_column_text(statement, 2, &item->type_identifier) || !database_statement_column_text(statement, 3, &item->role) || !database_statement_column_text(statement, 4, &item->source_header) || !database_statement_column_int64(statement, 5, &occurrence) || !database_statement_column_text(statement, 6, &item->provenance_kind) || !database_statement_column_text(statement, 7, &item->verification_status) || !database_statement_column_text(statement, 8, &item->integrated_at) || !database_statement_column_text(statement, 9, &item->entity_identifier) || !database_statement_column_text(statement, 10, &item->promotion_kind)) { evidence_observation_free(item); goto failure; } item->occurrence = (guint) occurrence; g_ptr_array_add(items, item); } database_statement_finalize(statement); return items; failure: evidence_entity_dao_set_database_error(dao, error, EVIDENCE_ENTITY_DAO_ERROR_EXECUTE, "Impossible de charger toutes les observations de la preuve"); database_statement_finalize(statement); g_clear_pointer(&items, g_ptr_array_unref); return NULL; }