LCOV - code coverage report
Current view: top level - src - db_compare.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 81.1 % 185 150
Test Date: 2026-07-12 01:01:34 Functions: 100.0 % 4 4
Branches: 78.6 % 154 121

             Branch data     Line data    Source code
       1                 :             : #include "precizer.h"
       2                 :             : 
       3                 :             : /**
       4                 :             :  * @brief Attaches a secondary database to the primary database connection.
       5                 :             :  *
       6                 :             :  * The path comes from the two `--compare` positional arguments. SQLite attaches
       7                 :             :  * the selected database under an internal schema alias such as `db1` or `db2`
       8                 :             :  *
       9                 :             :  * @param[in] db_A Index of the database path in `config->db_file_paths`.
      10                 :             :  * @param[in] db_B Database number (1 or 2) to be used in the ATTACH DATABASE command.
      11                 :             :  * @return Return structure indicating the operation status.
      12                 :             :  */
      13                 :         224 : static Return db_attach(
      14                 :             :         int db_A,
      15                 :             :         int db_B)
      16                 :             : {
      17                 :             :         /* This function was reviewed line by line by a human and is not AI-generated
      18                 :             :            Any change to this function requires separate explicit approval */
      19                 :             : 
      20                 :             :         /* Status returned by this function through provide()
      21                 :             :            Default value assumes successful completion */
      22                 :         224 :         Return status = SUCCESS;
      23                 :             : 
      24                 :         224 :         m_create(char,sql,MEMORY_STRING);
      25                 :         224 :         sqlite3_stmt *stmt = NULL;
      26                 :             : 
      27                 :         224 :         const memory *db_file_path = m_item_ro(memory,conf(db_file_paths),db_A);
      28                 :         224 :         size_t db_file_path_length = 0;
      29                 :             : 
      30         [ -  + ]:         224 :         if(db_file_path == NULL)
      31                 :             :         {
      32                 :           0 :                 report("Database path item is unavailable for index: %d",db_A);
      33                 :           0 :                 status = FAILURE;
      34                 :             :         }
      35                 :             : 
      36         [ +  - ]:         224 :         if(SUCCESS == status)
      37                 :             :         {
      38                 :         224 :                 status = m_string_length(db_file_path,&db_file_path_length);
      39                 :             :         }
      40                 :             : 
      41         [ +  - ]:         224 :         if(SUCCESS == status)
      42                 :             :         {
      43                 :         224 :                 run(m_formatted_string(sql,"ATTACH DATABASE ?1 AS db%d;",db_B));
      44                 :             :         }
      45                 :             : 
      46         [ +  - ]:         224 :         if(SUCCESS == status)
      47                 :             :         {
      48                 :         224 :                 int rc = sqlite3_prepare_v2(config->db,m_text(sql),-1,&stmt,NULL);
      49                 :             : 
      50         [ -  + ]:         224 :                 if(SQLITE_OK != rc)
      51                 :             :                 {
      52                 :           0 :                         log_sqlite_error(config->db,rc,NULL,"Failed to prepare attach statement");
      53                 :           0 :                         status = FAILURE;
      54                 :             :                 }
      55                 :             :         }
      56                 :             : 
      57         [ +  - ]:         224 :         if(SUCCESS == status)
      58                 :             :         {
      59                 :         224 :                 int rc = sqlite3_bind_text(stmt,1,m_text(db_file_path),(int)db_file_path_length,NULL);
      60                 :             : 
      61         [ -  + ]:         224 :                 if(SQLITE_OK != rc)
      62                 :             :                 {
      63                 :           0 :                         log_sqlite_error(config->db,rc,NULL,"Failed to bind database path in attach");
      64                 :           0 :                         status = FAILURE;
      65                 :             :                 }
      66                 :             :         }
      67                 :             : 
      68         [ +  - ]:         224 :         if(SUCCESS == status)
      69                 :             :         {
      70                 :         224 :                 int rc = sqlite3_step(stmt);
      71                 :             : 
      72         [ -  + ]:         224 :                 if(SQLITE_DONE != rc)
      73                 :             :                 {
      74                 :           0 :                         log_sqlite_error(config->db,rc,NULL,"Attach statement didn't return DONE");
      75                 :           0 :                         status = FAILURE;
      76                 :             :                 }
      77                 :             :         }
      78                 :             : 
      79                 :         224 :         int rc = sqlite3_finalize(stmt);
      80                 :             : 
      81   [ +  -  -  + ]:         224 :         if(SUCCESS == status && SQLITE_OK != rc)
      82                 :             :         {
      83                 :           0 :                 log_sqlite_error(config->db,rc,NULL,"Failed to finalize attach statement");
      84                 :           0 :                 status = FAILURE;
      85                 :             :         }
      86                 :             : 
      87                 :         224 :         call(m_del(sql));
      88                 :             : 
      89                 :         224 :         provide(status);
      90                 :             : }
      91                 :             : 
      92                 :             : /**
      93                 :             :  * @brief Detach database by alias
      94                 :             :  *
      95                 :             :  * @param[in] db_alias Attached database alias name
      96                 :             :  * @return Return status code
      97                 :             :  */
      98                 :         224 : static Return db_detach(const char *db_alias)
      99                 :             : {
     100                 :             :         /* This function was reviewed line by line by a human and is not AI-generated
     101                 :             :            Any change to this function requires separate explicit approval */
     102                 :             : 
     103                 :             :         /* Status returned by this function through provide()
     104                 :             :            Default value assumes successful completion */
     105                 :         224 :         Return status = SUCCESS;
     106                 :             : 
     107                 :         224 :         sqlite3_stmt *stmt = NULL;
     108                 :             : 
     109                 :         224 :         const char *sql = "DETACH DATABASE ?1;";
     110                 :             : 
     111                 :         224 :         int rc = sqlite3_prepare_v2(config->db,sql,-1,&stmt,NULL);
     112                 :             : 
     113         [ -  + ]:         224 :         if(SQLITE_OK != rc)
     114                 :             :         {
     115                 :           0 :                 log_sqlite_error(config->db,rc,NULL,"Failed to prepare detach statement");
     116                 :           0 :                 status = FAILURE;
     117                 :             :         }
     118                 :             : 
     119         [ +  - ]:         224 :         if(SUCCESS == status)
     120                 :             :         {
     121                 :         224 :                 rc = sqlite3_bind_text(stmt,1,db_alias,-1,SQLITE_STATIC);
     122                 :             : 
     123         [ -  + ]:         224 :                 if(SQLITE_OK != rc)
     124                 :             :                 {
     125                 :           0 :                         log_sqlite_error(config->db,rc,NULL,"Failed to bind database alias in detach");
     126                 :           0 :                         status = FAILURE;
     127                 :             :                 }
     128                 :             :         }
     129                 :             : 
     130         [ +  - ]:         224 :         if(SUCCESS == status)
     131                 :             :         {
     132                 :         224 :                 rc = sqlite3_step(stmt);
     133                 :             : 
     134         [ -  + ]:         224 :                 if(SQLITE_DONE != rc)
     135                 :             :                 {
     136                 :           0 :                         log_sqlite_error(config->db,rc,NULL,"Detach statement didn't return DONE");
     137                 :           0 :                         status = FAILURE;
     138                 :             :                 }
     139                 :             :         }
     140                 :             : 
     141                 :         224 :         rc = sqlite3_finalize(stmt);
     142                 :             : 
     143   [ +  -  -  + ]:         224 :         if(SUCCESS == status && SQLITE_OK != rc)
     144                 :             :         {
     145                 :           0 :                 log_sqlite_error(config->db,rc,NULL,"Failed to finalize detach statement");
     146                 :           0 :                 status = FAILURE;
     147                 :             :         }
     148                 :             : 
     149                 :         224 :         provide(status);
     150                 :             : }
     151                 :             : 
     152                 :             : /**
     153                 :             :  * @brief Run one compare query and print only the paths that pass output filters
     154                 :             :  *
     155                 :             :  * Executes the supplied comparison query, applies the shared --include/--ignore
     156                 :             :  * decision before reporting each row, and reports only visible relative paths.
     157                 :             :  * Only visible paths contribute to category state, so compare summaries and
     158                 :             :  * equality messages are evaluated against the filtered scope. The category
     159                 :             :  * heading is emitted only before the first visible path and stays visible in
     160                 :             :  * `--silent` only when `show_headings_in_silent` enables it
     161                 :             :  *
     162                 :             :  * @param[in] compare_sql SQL query that returns relative paths for one compare category
     163                 :             :  * @param[out] differences_found Set to `true` after the first visible path is printed
     164                 :             :  *             so hidden rows stay outside the reported comparison scope
     165                 :             :  * @param[in] show_headings_in_silent True to keep the category heading visible in `--silent`
     166                 :             :  * @param[in] heading_format Heading format string with two `%s` database-name slots
     167                 :             :  * @param[in] db_A_name First database name for the heading
     168                 :             :  * @param[in] db_B_name Second database name for the heading
     169                 :             :  * @return Return structure indicating the operation status
     170                 :             :  */
     171                 :         266 : static Return db_report_category(
     172                 :             :         const char *compare_sql,
     173                 :             :         bool       *differences_found,
     174                 :             :         const bool show_headings_in_silent,
     175                 :             :         const char *heading_format,
     176                 :             :         const char *db_A_name,
     177                 :             :         const char *db_B_name)
     178                 :             : {
     179                 :             :         /* This function was reviewed line by line by a human and is not AI-generated
     180                 :             :            Any change to this function requires separate explicit approval */
     181                 :             : 
     182                 :             :         /* Status returned by this function through provide()
     183                 :             :            Default value assumes successful completion */
     184                 :         266 :         Return status = SUCCESS;
     185                 :             : 
     186                 :         266 :         sqlite3_stmt *select_stmt = NULL;
     187                 :             : 
     188                 :         266 :         int rc = sqlite3_prepare_v2(config->db,compare_sql,-1,&select_stmt,NULL);
     189                 :             : 
     190         [ -  + ]:         266 :         if(SQLITE_OK != rc)
     191                 :             :         {
     192                 :           0 :                 log_sqlite_error(config->db,rc,NULL,"Can't prepare select statement");
     193                 :           0 :                 status = FAILURE;
     194                 :             :         }
     195                 :             : 
     196         [ +  - ]:         266 :         if(SUCCESS == status)
     197                 :             :         {
     198                 :         266 :                 bool first_visible_path = true;
     199                 :         266 :                 m_create(char,relative_path,MEMORY_STRING);
     200                 :             : 
     201         [ +  + ]:         426 :                 while(SQLITE_ROW == (rc = sqlite3_step(select_stmt)))
     202                 :             :                 {
     203                 :             :                         // Interrupt the loop smoothly
     204                 :             :                         // Interrupt when Ctrl+C
     205         [ -  + ]:         160 :                         if(global_interrupt_flag == true)
     206                 :             :                         {
     207                 :           0 :                                 break;
     208                 :             :                         }
     209                 :             : 
     210                 :         160 :                         const unsigned char *db_relative_path = sqlite3_column_text(select_stmt,0);
     211                 :             : 
     212         [ -  + ]:         160 :                         if(db_relative_path == NULL)
     213                 :             :                         {
     214                 :           0 :                                 rc = sqlite3_errcode(config->db);
     215                 :           0 :                                 log_sqlite_error(config->db,rc,NULL,"Failed to read relative path from select result");
     216                 :           0 :                                 status = FAILURE;
     217                 :           0 :                                 break;
     218                 :             :                         }
     219                 :             : 
     220                 :         160 :                         size_t relative_path_size = (size_t)sqlite3_column_bytes(select_stmt,0) + 1U;
     221                 :             : 
     222                 :         160 :                         run(m_copy_fixed_string(relative_path,relative_path_size,db_relative_path));
     223                 :             : 
     224         [ -  + ]:         160 :                         if(SUCCESS != status)
     225                 :             :                         {
     226                 :           0 :                                 break;
     227                 :             :                         }
     228                 :             : 
     229                 :         160 :                         bool ignore = false;
     230                 :             : 
     231                 :         160 :                         status = match_include_ignore(relative_path,
     232                 :             :                                 NULL,
     233                 :             :                                 &ignore);
     234                 :             : 
     235         [ -  + ]:         160 :                         if(SUCCESS != status)
     236                 :             :                         {
     237                 :           0 :                                 break;
     238                 :             :                         }
     239                 :             : 
     240         [ +  + ]:         160 :                         if(ignore == true)
     241                 :             :                         {
     242                 :          32 :                                 continue;
     243                 :             :                         }
     244                 :             : 
     245         [ +  + ]:         128 :                         if(first_visible_path == true)
     246                 :             :                         {
     247                 :         112 :                                 first_visible_path = false;
     248                 :             : 
     249                 :             :                                 // Outside --silent the heading is always shown
     250                 :             :                                 // In --silent it stays only when multiple compare categories can mix together in one output
     251   [ +  +  +  + ]:         112 :                                 if((rational_logger_mode & SILENT) == 0 || show_headings_in_silent == true)
     252                 :             :                                 {
     253                 :         106 :                                         slog(EVERY|VISIBLE_IN_SILENT,heading_format,db_A_name,db_B_name);
     254                 :             :                                 }
     255                 :             :                         }
     256                 :             : 
     257                 :         128 :                         *differences_found = true;
     258                 :         128 :                         slog(EVERY|UNDECOR|VISIBLE_IN_SILENT,"%s\n",m_text(relative_path));
     259                 :             :                 }
     260                 :             : 
     261                 :         266 :                 m_del(relative_path);
     262                 :             : 
     263   [ +  -  +  -  :         266 :                 if(SUCCESS == status && global_interrupt_flag == false && SQLITE_DONE != rc)
                   -  + ]
     264                 :             :                 {
     265                 :           0 :                         log_sqlite_error(config->db,rc,NULL,"Select statement didn't finish with DONE");
     266                 :           0 :                         status = FAILURE;
     267                 :             :                 }
     268                 :             :         }
     269                 :             : 
     270                 :         266 :         rc = sqlite3_finalize(select_stmt);
     271                 :             : 
     272         [ -  + ]:         266 :         if(SQLITE_OK != rc)
     273                 :             :         {
     274                 :           0 :                 log_sqlite_error(config->db,rc,NULL,"Failed to finalize SQLite statement");
     275                 :           0 :                 status = FAILURE;
     276                 :             :         }
     277                 :             : 
     278                 :         266 :         provide(status);
     279                 :             : }
     280                 :             : 
     281                 :             : /**
     282                 :             :  * @brief Compare the two databases selected by `--compare`
     283                 :             :  *
     284                 :             :  * @details The two database file paths come from `config->db_file_paths`, not
     285                 :             :  * from traversal roots. The function verifies both files, attaches them to the
     286                 :             :  * primary SQLite connection, reports requested difference categories, and
     287                 :             :  * prints summary lines for missing paths and checksum mismatches
     288                 :             :  *
     289                 :             :  * The comparison scope can be limited with `--compare-filter` and further
     290                 :             :  * narrowed by `--ignore` and `--include`; without filters the function checks
     291                 :             :  * first-source paths, second-source paths, and SHA512 mismatches. For example,
     292                 :             :  * `precizer --compare first.db second.db --compare-filter=sha512` validates both DB
     293                 :             :  * files and reports checksum differences for paths present in both databases
     294                 :             :  *
     295                 :             :  * @return Return status code
     296                 :             :  */
     297                 :         484 : Return db_compare(void)
     298                 :             : {
     299                 :             :         /* This function was reviewed line by line by a human and is not AI-generated
     300                 :             :            Any change to this function requires separate explicit approval */
     301                 :             : 
     302                 :             :         /* Status returned by this function through provide()
     303                 :             :            Default value assumes successful completion */
     304                 :         484 :         Return status = SUCCESS;
     305                 :             : 
     306                 :         484 :         bool attached_db1 = false;
     307                 :         484 :         bool attached_db2 = false;
     308                 :             : 
     309                 :             :         /* Interrupt the function smoothly */
     310                 :             :         /* Interrupt when Ctrl+C */
     311         [ -  + ]:         484 :         if(global_interrupt_flag == true)
     312                 :             :         {
     313                 :           0 :                 provide(status);
     314                 :             :         }
     315                 :             : 
     316                 :             :         /* Skip if comparison mode is not enabled */
     317         [ +  + ]:         484 :         if(config->compare != true)
     318                 :             :         {
     319                 :         366 :                 slog(TRACE,"Database comparison mode is not enabled. Skipping comparison\n");
     320                 :         366 :                 provide(status);
     321                 :             :         }
     322                 :             : 
     323                 :         118 :         slog(EVERY,"The comparison of %s and %s databases is starting…\n",config->db_file_names[0],config->db_file_names[1]);
     324                 :             : 
     325                 :             :         /* Validate database paths */
     326   [ +  +  +  + ]:         464 :         m_string_array_foreach(conf(db_file_paths),db_file_path)
     327                 :             :         {
     328                 :         234 :                 const char *db_file_path_text = m_text(db_file_path);
     329                 :             : 
     330         [ -  + ]:         234 :                 if(NOT_FOUND == file_availability(db_file_path_text,NULL,SHOULD_BE_A_FILE))
     331                 :             :                 {
     332                 :           0 :                         slog(ERROR,"The database file %s is either inaccessible or not a valid file\n",
     333                 :             :                                 db_file_path_text);
     334                 :           0 :                         status = FAILURE;
     335                 :           0 :                         break;
     336                 :             :                 }
     337                 :             : 
     338         [ +  - ]:         234 :                 if(SUCCESS == status)
     339                 :             :                 {
     340                 :             :                         /*
     341                 :             :                          * Validate the integrity of the database file
     342                 :             :                          */
     343                 :         234 :                         status = db_integrity_check(db_file_path_text);
     344                 :             : 
     345         [ +  + ]:         234 :                         if(SUCCESS != status)
     346                 :             :                         {
     347                 :           6 :                                 break;
     348                 :             :                         }
     349                 :             :                 }
     350                 :             :         }
     351                 :             : 
     352                 :             :         /* Attach databases */
     353         [ +  + ]:         118 :         if(SUCCESS == status)
     354                 :             :         {
     355                 :             :                 // Attach the database 1
     356                 :         112 :                 status = db_attach(0,1);
     357                 :             : 
     358         [ +  - ]:         112 :                 if(SUCCESS == status)
     359                 :             :                 {
     360                 :         112 :                         attached_db1 = true;
     361                 :             :                 }
     362                 :             :         }
     363                 :             : 
     364         [ +  + ]:         118 :         if(SUCCESS == status)
     365                 :             :         {
     366                 :             :                 // Attach the database 2
     367                 :         112 :                 status = db_attach(1,2);
     368                 :             : 
     369         [ +  - ]:         112 :                 if(SUCCESS == status)
     370                 :             :                 {
     371                 :         112 :                         attached_db2 = true;
     372                 :             :                 }
     373                 :             :         }
     374                 :             : 
     375                 :             :         /* SQL queries for comparison */
     376                 :         118 :         const char *compare_A_sql = "SELECT a.relative_path "
     377                 :             :                 "FROM db2.files AS a "
     378                 :             :                 "LEFT JOIN db1.files AS b on b.relative_path = a.relative_path "
     379                 :             :                 "WHERE b.relative_path IS NULL "
     380                 :             :                 "ORDER BY a.relative_path ASC;";
     381                 :             : 
     382                 :         118 :         const char *compare_B_sql = "SELECT a.relative_path "
     383                 :             :                 "FROM db1.files AS a "
     384                 :             :                 "LEFT join db2.files AS b on b.relative_path = a.relative_path "
     385                 :             :                 "WHERE b.relative_path IS NULL "
     386                 :             :                 "ORDER BY a.relative_path ASC;";
     387                 :             : 
     388                 :             :         // True when user provided at least one --compare-filter option.
     389                 :             :         // False means default compare mode: all three categories are enabled.
     390                 :         118 :         const bool filter_specified = config->compare_filter != CF_NONE_SPECIFIED;
     391                 :             : 
     392                 :             :         // Enables "first-source" category:
     393                 :             :         // show paths that exist in db1 but are missing in db2.
     394                 :             :         // This category is active either explicitly by filter or by default mode.
     395                 :         236 :         const bool check_first_source = (config->compare_filter & CF_FIRST_SOURCE)
     396   [ +  +  +  + ]:         118 :                 || filter_specified == false;
     397                 :             : 
     398                 :             :         // Enables "second-source" category:
     399                 :             :         // show paths that exist in db2 but are missing in db1.
     400                 :             :         // This category is active either explicitly by filter or by default mode.
     401                 :         236 :         const bool check_second_source = (config->compare_filter & CF_SECOND_SOURCE)
     402   [ +  +  +  + ]:         118 :                 || filter_specified == false;
     403                 :             : 
     404                 :             :         // Enables checksum verification category for common relative paths.
     405                 :             :         // Active either explicitly by checksum filter or by default mode.
     406                 :         236 :         const bool verify_checksum_consistency = (config->compare_filter & CF_CHECKSUM_MISMATCH)
     407   [ +  +  +  + ]:         118 :                 || filter_specified == false;
     408                 :             : 
     409                 :             :         /*
     410                 :             :          * Count the enabled comparison categories before producing output.
     411                 :             :          * In silent mode, a heading is unnecessary when only one category can
     412                 :             :          * produce paths because every printed path belongs to that category.
     413                 :             :          * When several categories are enabled, keep headings visible so paths from
     414                 :             :          * different categories remain distinguishable
     415                 :             :          */
     416                 :         118 :         unsigned int active_compare_categories = 0u;
     417                 :             : 
     418         [ +  + ]:         118 :         if(check_first_source == true)
     419                 :             :         {
     420                 :          96 :                 active_compare_categories++;
     421                 :             :         }
     422                 :             : 
     423         [ +  + ]:         118 :         if(check_second_source == true)
     424                 :             :         {
     425                 :          96 :                 active_compare_categories++;
     426                 :             :         }
     427                 :             : 
     428         [ +  + ]:         118 :         if(verify_checksum_consistency == true)
     429                 :             :         {
     430                 :          92 :                 active_compare_categories++;
     431                 :             :         }
     432                 :             : 
     433                 :             :         // Keeps category headings visible only when silent output can mix multiple categories
     434                 :         118 :         const bool show_headings_in_silent = active_compare_categories > 1u;
     435                 :             : 
     436                 :             :         // Comparison result flags grouped in one place for summary evaluation
     437                 :         118 :         bool first_source_differences_found = false;
     438                 :         118 :         bool second_source_differences_found = false;
     439                 :         118 :         bool checksum_mismatches_found = false;
     440                 :             : 
     441                 :             :         /* Compare files existence between databases */
     442         [ +  + ]:         118 :         if(check_first_source == true)
     443                 :             :         {
     444                 :          96 :                 run(db_report_category(compare_B_sql,
     445                 :             :                         &first_source_differences_found,
     446                 :             :                         show_headings_in_silent,
     447                 :             :                         BOLD "These files are no longer in the %s but still exist in the %s" RESET "\n",
     448                 :             :                         config->db_file_names[1],
     449                 :             :                         config->db_file_names[0]));
     450                 :             :         }
     451                 :             : 
     452         [ +  + ]:         118 :         if(check_second_source == true)
     453                 :             :         {
     454                 :          96 :                 run(db_report_category(compare_A_sql,
     455                 :             :                         &second_source_differences_found,
     456                 :             :                         show_headings_in_silent,
     457                 :             :                         BOLD "These files are no longer in the %s but still exist in the %s" RESET "\n",
     458                 :             :                         config->db_file_names[0],
     459                 :             :                         config->db_file_names[1]));
     460                 :             :         }
     461                 :             : 
     462                 :             : #if 0
     463                 :             :         // Disabled multi-root path index implementation
     464                 :             :         const char *compare_checksums_sql = "select a.relative_path from db2.files a inner join db1.files b"
     465                 :             :                 " on b.relative_path = a.relative_path "
     466                 :             :                 " and b.sha512 is not a.sha512"
     467                 :             :                 " order by a.relative_path asc;";
     468                 :             : 
     469                 :             :         const char *compare_checksums_sql = "SELECT p.path,f1.relative_path "
     470                 :             :                 "FROM db1.files AS f1 "
     471                 :             :                 "JOIN db1.paths AS p ON f1.path_prefix_index = p.ID "
     472                 :             :                 "JOIN db2.files AS f2 ON f1.relative_path = f2.relative_path "
     473                 :             :                 "JOIN db2.paths AS p2 ON f2.path_prefix_index = p2.ID "
     474                 :             :                 "WHERE f1.sha512 IS NOT f2.sha512 AND p.path = p2.path "
     475                 :             :                 "ORDER BY p.path,f1.relative_path ASC;";
     476                 :             : #else
     477                 :             :         // One PATH solution
     478                 :         118 :         const char *compare_checksums_sql = "SELECT a.relative_path "
     479                 :             :                 "FROM db2.files AS a "
     480                 :             :                 "INNER JOIN db1.files AS b ON b.relative_path = a.relative_path "
     481                 :             :                 "WHERE b.sha512 IS NOT a.sha512 "
     482                 :             :                 "ORDER BY a.relative_path ASC;";
     483                 :             : #endif
     484                 :             : 
     485         [ +  + ]:         118 :         if(verify_checksum_consistency == true)
     486                 :             :         {
     487                 :          92 :                 run(db_report_category(compare_checksums_sql,
     488                 :             :                         &checksum_mismatches_found,
     489                 :             :                         show_headings_in_silent,
     490                 :             :                         BOLD "The SHA512 checksums of these files do not match between %s and %s" RESET "\n",
     491                 :             :                         config->db_file_names[0],
     492                 :             :                         config->db_file_names[1]));
     493                 :             :         }
     494                 :             : 
     495                 :             :         /* Cleanup */
     496         [ +  + ]:         118 :         if(attached_db1 == true)
     497                 :             :         {
     498                 :         112 :                 call(db_detach("db1"));
     499                 :             :         }
     500                 :             : 
     501         [ +  + ]:         118 :         if(attached_db2 == true)
     502                 :             :         {
     503                 :         112 :                 call(db_detach("db2"));
     504                 :             :         }
     505                 :             : 
     506                 :             :         /* Output results */
     507         [ +  + ]:         118 :         if(SUCCESS == status)
     508                 :             :         {
     509                 :         112 :                 const bool full_compare_scope = check_first_source == true
     510         [ +  + ]:          90 :                         && check_second_source == true
     511   [ +  +  +  + ]:         202 :                         && verify_checksum_consistency == true;
     512                 :             : 
     513         [ +  + ]:         112 :                 if(full_compare_scope == true
     514         [ +  + ]:          70 :                         && first_source_differences_found == false
     515         [ +  + ]:          50 :                         && second_source_differences_found == false
     516         [ +  + ]:          36 :                         && checksum_mismatches_found == false)
     517                 :             :                 {
     518                 :          28 :                         slog(EVERY,BOLD "All files are identical against %s and %s" RESET "\n",
     519                 :             :                                 config->db_file_names[0],
     520                 :             :                                 config->db_file_names[1]);
     521                 :             : 
     522         [ +  + ]:          84 :                 } else if(full_compare_scope == false){
     523                 :             : 
     524         [ +  + ]:          42 :                         if(check_first_source == true
     525         [ +  + ]:          20 :                                 && first_source_differences_found == false)
     526                 :             :                         {
     527                 :           8 :                                 slog(EVERY,BOLD "No first-source differences found between %s and %s" RESET "\n",
     528                 :             :                                         config->db_file_names[0],
     529                 :             :                                         config->db_file_names[1]);
     530                 :             :                         }
     531                 :             : 
     532         [ +  + ]:          42 :                         if(check_second_source == true
     533         [ +  + ]:          20 :                                 && second_source_differences_found == false)
     534                 :             :                         {
     535                 :           8 :                                 slog(EVERY,BOLD "No second-source differences found between %s and %s" RESET "\n",
     536                 :             :                                         config->db_file_names[0],
     537                 :             :                                         config->db_file_names[1]);
     538                 :             :                         }
     539                 :             :                 }
     540                 :             : 
     541   [ +  +  +  + ]:         112 :                 if(verify_checksum_consistency == true && checksum_mismatches_found == false)
     542                 :             :                 {
     543                 :          46 :                         slog(EVERY,BOLD "All SHA512 checksums of files are identical against %s and %s" RESET "\n",
     544                 :             :                                 config->db_file_names[0],
     545                 :             :                                 config->db_file_names[1]);
     546                 :             :                 }
     547                 :             : 
     548         [ +  + ]:         112 :                 if(full_compare_scope == true
     549         [ +  + ]:          70 :                         && first_source_differences_found == false
     550         [ +  + ]:          50 :                         && second_source_differences_found == false
     551         [ +  + ]:          36 :                         && checksum_mismatches_found == false)
     552                 :             :                 {
     553                 :          28 :                         slog(EVERY,BOLD "The databases %s and %s are absolutely equal" RESET "\n",
     554                 :             :                                 config->db_file_names[0],
     555                 :             :                                 config->db_file_names[1]);
     556                 :             :                 }
     557                 :             :         }
     558                 :             : 
     559                 :         118 :         slog(EVERY,"Comparison of %s and %s databases is complete\n",
     560                 :             :                 config->db_file_names[0],
     561                 :             :                 config->db_file_names[1]);
     562                 :             : 
     563                 :         118 :         provide(status);
     564                 :             : }
        

Generated by: LCOV version 2.0-1