## utils/hs_cleanup/hs_cleanup.c

/* [<][>][^][v][top][bottom][index][help] */

## FUNCTIONS

This source file includes following functions.

1. update_prev_serial_of_next_object
2. update_serial_in_last
3. update_serial_in_history
4. archive_history
5. archive_last
6. delete_history_entry
7. delete_last_entry
8. main
9. usage
10. get_highest_serial_before_date
11. archive_serials_and_history
12. fetch_timestamp_from_last
13. get_highest_object_id
14. check_if_next_is_deletion
15. create_archive_tables
16. create_table
17. update_history_archive_sequence_id_and_timestamp
18. update_history_archive_object_id_and_sequence_id
19. update_prev_serial_of_object
20. update_serial_of_object
21. archive_serial
22. archive_failed_transaction
23. copy_into_history_archive
24. copy_deleted_object_into_history_archive
25. delete_serial_entry
26. delete_failed_transaction_entry
27. delete_entry_from_object_table
28. delete_archived_objects
29. delete_serial_archive_entry
30. delete_history_archive_entry
31. find_unreferenced_history_entries
32. delete_unreferenced_history_entries
33. PushTblObjList
34. delete_dummy_history_objects
35. lock_last_history_serial_tables
36. unlock_all_tables
37. optimize_sql_table
38. execute_sql_query
39. execute_sql_command
40. create_auxiliary_table
41. reset_auxiliary_table
42. drop_auxiliary_table
43. update_hs_auxiliary_checkpoint
44. crash_recovery
45. exists_checkpointing_table
46. get_smallest_serial
47. get_random_number_in_range
48. do_crash

       1 /***************************************
       2   $Revision: 1.33 $
       3
       4   History/Serial Cleanup (hs_cleanup). This utility archives serials
       5   and history entries, and deletes them from the live database.
       6
       7   Status: COMPLETE, NOT REVUED, NOT FULLY TESTED
       8
       9   ******************/ /******************
      10   Filename            : hs_cleanup.c
      11   Authors             : Daniele Arena
      12   OSs Tested          : Solaris 7
      13   ******************/ /******************
      14   Copyright (c) 2000, 2001                        RIPE NCC
      15
      16   All Rights Reserved
      17
      18   Permission to use, copy, modify, and distribute this software and its
      19   documentation for any purpose and without fee is hereby granted,
      20   provided that the above copyright notice appear in all copies and that
      21   both that copyright notice and this permission notice appear in
      22   supporting documentation, and that the name of the author not be
      23   used in advertising or publicity pertaining to distribution of the
      24   software without specific, written prior permission.
      25
      26   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
      27   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
      28   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
      29   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
      30   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      31   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      32   ***************************************/
      33
      34
      35 /********** INCLUDES **********/
      36
      37 /* Standard includes */
      38 #include <stdio.h>
      39 #include <stdlib.h>
      40 #include <string.h>
      41 #include <sys/time.h> /* for time() */
      42 #include <math.h> /* for floor() */
      43 #include <unistd.h> /* for sleep() */
      44
      45 /* RIP includes */
      46 #include "mysql_driver.h"
      47 #include "stubs.h"
      48
      49
      50 /********** DEFINES **********/
      51
      52 /* Default settings for the SQL DB */
      53 #define MYSQL_HOST "myhost.mydb.net"
      54 #define MYSQL_PORT 3306
      55 #define MYSQL_USER "sqluser"
      56 #define MYSQL_PSWD "sqlpswd"
      57 #define MYSQL_DB "sqldb"
      58
      59 /* String sizes */
      60 #define STR_S   63
      61 #define STR_M   255
      62 #define STR_L   1023
      63 #define STR_XL  4095
      64 #define STR_XXL 16383
      65
      66 /* Maximum allowed length of a SQL command */
      67 #define MAXCMDLEN 8192
      68
      69
      70 /*
      71  * Standard time constants:
      72  * 1min = 60s
      73  * 1hr = 3600s
      74  * 1day = 86400s
      75  * 1wk = 604800s
      76  */
      77
      78 #define MINUTE 60
      79 #define HOUR 3600
      80 #define DAY 86400
      81 #define WEEK 604800
      82
      83 /* For debugging purposes */
      84 #define DEBUG 0
      85
      86 /* If this flag is set, older-and-already-archived history object won't get deleted.
      87    XXX: currently this *must* be set, otherwise weird things happen. To be fixed. */
      88 #define ARCHIVE_ONLY 1
      89
      90 /* Object types from RIP */
      91 #define OBJ_TYPE_DUMMY 100
      92
      93 /* For crash-recovery tests */
      94
      95 /* Activate this flag if you want to test crash-recovery */
      96 #define CRASHTEST 0
      97
      98 /* Helpers for blackbox crashtest */
      99 #define IS_LAST_STEP 1
     100 #define MAX_STEPS 8
     101
     102 /********** ENUMS **********/
     103
     104 /* List of possible "atlast" values */
     105 enum {
     106   IN_HISTORY_TABLE = 0,
     107   IN_LAST_TABLE,
     108   IN_FAILED_TRANSACTION_TABLE,
     109   IN_UNDEF_TABLE
     110 };
     111
     112 const char *atlast_table[] = { "history",
     113                                "last",
     114                                "failed_transaction" };
     115
     116 /* List of operations for a serial in RIP */
     117 enum {
     118   OP_NULL = 0,
     119   OP_ADD,
     120   OP_DELETE,
     121   OP_UPDATE
     122 };
     123
     124 /* For checkpointing/crash-recovery:
     125  * we mark the actions to be taken if recovering
     126  */
     127 enum {
     128   CHKP_NOOP = 0,
     129   CHKP_DELETE_FROM_ARCHIVE,
     130   CHKP_DELETE_FROM_LIVE,
     131   CHKP_DELETE_FROM_LIVE_ONLY_SERIAL,
     132   CHKP_DONE
     133 };
     134
     135
     136
     137 /********** TYPEDEFS **********/
     138
     139 /* A structure to hold an object from last or history table */
     140 typedef struct table_object *tblobjPtr;
     141
     142 typedef struct table_object {
     143   long objid;
     144   long seqid;
     145   tblobjPtr next;
     146 } tblObjList;
     147
     148
     149
     150 /********** GLOBAL VARIABLES **********/
     151
     152 SQ_connection_t *connection;
     153 int debug = DEBUG;
     154 int archive_only = ARCHIVE_ONLY;
     155 long highest_objid;
     156 /* For crash recovery test */
     157 long crashing_serial;
     158 int crash_position;
     159 int code_location;
     160 int case_branch;
     161
     162
     163 /********** FUNCTION DEFINITIONS **********/
     164
     165
     166 /*** Main functions ***/
     167 int main (int argc, char *argv[]);
     168 void usage(char *argv[]);
     169
     170 /*** Archiving algorithm functions ***/
     171 static int get_highest_serial_before_date(long *highest_serial_ptr, long date);
     172 static int archive_serials_and_history(long highest_serial);
     173 static long fetch_timestamp_from_last(long obj_id, long ser_id);
     174 static long get_highest_object_id();
     175 static int check_if_next_is_deletion (long obj_id, long seq_id);
     176
     177 /*** Table creation ***/
     178 static int create_archive_tables();
     179 static int create_table(const char *cmd);
     180
     181 /*** Object update ***/
     182 static int update_history_archive_sequence_id_and_timestamp(long ser_id, long new_seq_id, long new_timestamp);
     183 static int update_history_archive_object_id_and_sequence_id (long ser_id, long new_obj_id, long new_seq_id);
     184 static int update_prev_serial_of_object (long obj_id, long seq_id, long prev_ser, const char *tablename);
     185 static int update_serial_of_object (long obj_id, long seq_id, long ser_id, const char *tablename);
     186 #define update_prev_serial_of_next_object(obj_id, seq_id, prev_ser, tablename) update_prev_serial_of_object(obj_id, seq_id+1, prev_ser, tablename)
         /* [<][>][^][v][top][bottom][index][help] */
     187 #define update_serial_in_last(obj_id, seq_id, ser_id) update_serial_of_object(obj_id, seq_id, ser_id, "last")
         /* [<][>][^][v][top][bottom][index][help] */
     188 #define update_serial_in_history(obj_id, seq_id, ser_id) update_serial_of_object(obj_id, seq_id, ser_id, "history")
         /* [<][>][^][v][top][bottom][index][help] */
     189
     190 /*** Object archiving ***/
     191 static int archive_serial(long ser_id, long obj_id, long seq_id, int op);
     192 static int archive_failed_transaction(long ser_id);
     193 static int copy_into_history_archive(long ser_id, long obj_id, long seq_id, const char *tablename);
     194 static int copy_deleted_object_into_history_archive (long ser_id, long obj_id, long seq_id, const char *tablename);
     195 #define archive_history(ser_id, obj_id, seq_id) copy_into_history_archive(ser_id, obj_id, seq_id, "history")
         /* [<][>][^][v][top][bottom][index][help] */
     196 #define archive_last(ser_id, obj_id, seq_id) copy_into_history_archive(ser_id, obj_id, seq_id, "last")
         /* [<][>][^][v][top][bottom][index][help] */
     197
     198 /*** Object deletion ***/
     199 static int delete_serial_entry(long ser_id);
     200 static int delete_failed_transaction_entry (long ser_id);
     201 static int delete_entry_from_object_table(long obj_id, long seq_id, const char* tablename);
     202 static int delete_archived_objects(long ser_id);
     203 static int delete_serial_archive_entry(long ser_id);
     204 static int delete_history_archive_entry(long ser_id);
     205 #define delete_history_entry(obj_id, seq_id) delete_entry_from_object_table(obj_id, seq_id, "history")
         /* [<][>][^][v][top][bottom][index][help] */
     206 #define delete_last_entry(obj_id, seq_id) delete_entry_from_object_table(obj_id, seq_id, "last")
         /* [<][>][^][v][top][bottom][index][help] */
     207
     208 /*** Handling of older, unreferenced history entries ***/
     209 static tblObjList *find_unreferenced_history_entries(long date);
     210 static int delete_unreferenced_history_entries(tblObjList *objectsToDelete);
     211
     212 /*** Handling of dummy objects ***/
     213 static int delete_dummy_history_objects();
     214
     215 /*** Interactions with SQL DB ***/
     216 static int lock_last_history_serial_tables();
     217 static int unlock_all_tables();
     218 static int optimize_sql_table(const char* tablename);
     219
     220 /*** SQL interfaces ***/
     221 static int execute_sql_command(const char *cmd);
     222 static int execute_sql_query(const char *query, SQ_result_set_t **result_ptr);
     223
     224 /*** Checkpointing ***/
     225 static int create_auxiliary_table();
     226 static int reset_auxiliary_table();
     227 static int drop_auxiliary_table();
     228 static int update_hs_auxiliary_checkpoint(long ser_id, long obj_id, long seq_id, int atlast, int checkpoint);
     229 static int crash_recovery();
     230 static int exists_checkpointing_table();
     231 /* Checkpointing test */
     232 static long get_smallest_serial();
     233 static int get_random_number_in_range(int num1, int num2, int seed);
     234 static void do_crash(long crashserial, int is_last_step);
     235
     236
     237 /********** KNOWN BUGS AND LIMITATIONS **********/
     238
     239 /* XXX Fixme:
     240    - Subroutine (+option) to warn that the size is bigger than a watermark
     241      (needed to burn CDs)
     242    */
     243
     244
     245
     246
     247 /********** THE CODE **********/
     248
     249
     250 /*** Main functions ***/
     251
     252 /****
     253  *
     254  * main()
     255  *
     256  ****/
     257
     258 int main (int argc, char *argv[])
         /* [<][>][^][v][top][bottom][index][help] */
     259 {
     260
     261   int ch, rc;
     262   long highest_serial;
     263   short errflg = 1;
     264   short tflg = 0;
     265   extern char *optarg;
     266   extern int optind;
     267   time_t now = time(0);
     268   time_t date;
     269   time_t lapse;
     270
     271   char sqlhost[STR_M] = MYSQL_HOST;
     272   int sqlport = MYSQL_PORT;
     273   char sqluser[STR_M] = MYSQL_USER;
     274   char sqlpswd[STR_M] = MYSQL_PSWD;
     275   char sqldb[STR_M] = MYSQL_DB;
     276
     277   tblObjList *objectsToDelete;
     278   long serialsArchived;
     279   /* tblObjList *tmpObj; */
     280
     281   /* Get options */
     282
     283   while ((ch = getopt(argc, argv, "?T:S:M:H:D:W:h:P:u:p:d:")) != EOF )
     284     switch((char)ch)
     285       {
     286       case 'T':
     287         if (tflg)
     288           errflg++;
     289         else
     290           {
     291             tflg++;
     292             errflg = 0;
     293             date = atol (optarg);
     294           }
     295         break;
     296       case 'S':
     297         if (tflg)
     298           errflg++;
     299         else
     300           {
     301             tflg++;
     302             errflg = 0;
     303             lapse = atol (optarg);
     304             date = now - lapse;
     305           }
     306         break;
     307       case 'M':
     308         if (tflg)
     309           errflg++;
     310         else
     311           {
     312             tflg++;
     313             errflg = 0;
     314             lapse = atol (optarg);
     315             date = now - lapse * MINUTE;
     316           }
     317         break;
     318       case 'H':
     319         if (tflg)
     320           errflg++;
     321         else
     322           {
     323             tflg++;
     324             errflg = 0;
     325             lapse = atol (optarg);
     326             date = now - lapse * HOUR;
     327           }
     328         break;
     329       case 'D':
     330         if (tflg)
     331           errflg++;
     332         else
     333           {
     334             tflg++;
     335             errflg = 0;
     336             lapse = atol (optarg);
     337             date = now - lapse * DAY;
     338           }
     339         break;
     340       case 'W':
     341         if (tflg)
     342           errflg++;
     343         else
     344           {
     345             tflg++;
     346             errflg = 0;
     347             lapse = atol (optarg);
     348             date = now - lapse * WEEK;
     349           }
     350         break;
     351       case 'h':
     352         sprintf (sqlhost,"%s",optarg);
     353         break;
     354       case 'P':
     355         sqlport = atoi(optarg);
     356         break;
     357       case 'u':
     358         sprintf (sqluser,"%s",optarg);
     359         break;
     360       case 'p':
     361         sprintf (sqlpswd,"%s",optarg);
     362         break;
     363       case 'd':
     364         sprintf (sqldb,"%s",optarg);
     365         break;
     366       case '?':
     367       default:
     368         errflg++;
     369       }
     370
     371 if (errflg)
     372   usage(argv);
     373
     374
     375   /* Initialize connection */
     376   connection = SQ_get_connection(sqlhost, sqlport,sqldb,sqluser,sqlpswd);
     377
     378   /* Create tables for history and serials archives
     379    * if they do not exist */
     380   if ((rc = create_archive_tables()) != 0)
     381     { return(rc); }
     382
     383   /* XXX Call remadmin interface and stop updates
     384    * (currently done externally via a wrapping script) */
     385   /* XXX If retcode is successful, go on */
     386
     387   /* Crash recovery handling. */
     388   /* fprintf (stderr, "Starting crash recovery...\n"); */
     389   crash_recovery();
     390   /* fprintf (stderr, "Crash recovery done.\n"); */
     391
     392   /* Deal with very old history entries, those that do not even have a corresponding
     393    * serial. These are entries which had been archived when they were in the "last" table,
     394    * and have in the meanwhile been updated. We have to:
     395    *    - Update prev_serial of their next object
     396    *    - Delete them!
     397    */
     398
     399   objectsToDelete = find_unreferenced_history_entries((long) date);
     400
     401   /* printf ("Elements to be deleted:\n");
     402      for (tmpObj = objectsToDelete; tmpObj != NULL; tmpObj = tmpObj->next)
     403      {
     404      printf ("objid: %ld, seqid: %ld\n", tmpObj->objid, tmpObj->seqid);
     405      } */
     406
     407
     408   /* Get the biggest serial for which the history or last timestamp is lower than
     409    * the defined timestamp
     410    */
     411
     412   if ((rc = get_highest_serial_before_date(&highest_serial, (long) date)) != 0)
     413     { return(rc); }
     414   printf ("Highest serial ID: %ld\n",highest_serial);
     415
     416   highest_objid = get_highest_object_id();
     417   printf ("Highest object_id: %ld\n",highest_objid);
     418
     419   /* Execute the archiving commands */
     420
     421   serialsArchived = archive_serials_and_history(highest_serial);
     422   if (serialsArchived>0)printf ("Serials archived: %ld [%ld - %ld]\n",serialsArchived, highest_serial-serialsArchived, highest_serial-1);
     423   else printf ("Serials archived: 0 \n");
     424
     425   /* Optimize history serial and last tables: there might have been many deletions */
     426   /* XXX: optimizing the 'last' table takes about 300 seconds now, during
     427           which time queries are blocked - we need another way to do
     428           this - SK 2001-03-29 */
     429   /*optimize_sql_table("serials");*/
     430   /*optimize_sql_table("history");*/
     431   /*optimize_sql_table("last");*/
     432
     433   /* XXX Call remadmin interface and restart updates
     434    * (currently done externally via a wrapping script) */
     435   /* XXX If retcode is not successful, go on, but issue a warning */
     436
     437   /* Delete the unreferenced history entries. Must be done at the end. */
     438   /* XXX Bug here. The older entries cannot be deleted or they wreak havoc.
     439    * archive_only must be 1. */
     440   if (! archive_only)
     441     delete_unreferenced_history_entries(objectsToDelete);
     442
     443   /* Delete dummy history objects */
     444   delete_dummy_history_objects();
     445
     446   /* OK, it's over. */
     447
     448   drop_auxiliary_table();
     449
     450   SQ_close_connection(connection);
     451   printf ("\nProgram done.\n");
     452   return(0);
     453
     454 } /* main() */
     455
     456
     457
     458 /****
     459  *
     460  * usage(): help for command usage
     461  * Needs argv[] for the command path supplied
     462  *
     463  ****/
     464
     465 void usage(char *argv[])
         /* [<][>][^][v][top][bottom][index][help] */
     466 {
     467
     468   printf ("Usage: \n\n");
     469   printf ("  %s [-?] [-h host] [-P port] [-u user] [-p password] [-d database]\n", argv[0]);
     470   printf ("     [-T date|-S seconds|-M minutes|-H hours|-D days|-W weeks] \n");
     471
     472   printf ("\nGeneral options:\n");
     473   printf ("   -?: This text\n");
     474
     475   printf ("\nSQL Options:\n");
     476   printf ("   -h: host \t\t(default: %s)\n",MYSQL_HOST);
     477   printf ("   -P: port \t\t(default: %d)\n",MYSQL_PORT);
     478   printf ("   -u: user \t\t(default: %s)\n",MYSQL_USER);
     479   printf ("   -p: password \t(default: %s)\n",MYSQL_PSWD);
     480   printf ("   -d: database name \t(default: %s)\n",MYSQL_DB);
     481
     482   printf ("\nTime-related options: (one and only one must be specified)\n");
     483   printf ("   -T date: Date before which to archive (secs from the Epoch)\n");
     484   printf ("   -S seconds: Seconds elapsed between the date to archive and now\n");
     485   printf ("   -M minutes: Minutes elapsed between the date to archive and now\n");
     486   printf ("   -H hours: Hours elapsed between the date to archive and now\n");
     487   printf ("   -D days: Days elapsed between the date to archive and now\n");
     488   printf ("   -W weeks: Weeks elapsed between the date to archive and now\n");
     489   exit(1);
     490
     491 } /* usage() */
     492
     493
     494
     495
     496
     497 /*** Archiving algorithm functions ***/
     498
     499 /****
     500  *
     501  * get_highest_serial_before_date()
     502  * We get the biggest serial for which the history or last timestamp is lower than
     503  * the defined timestamp
     504  *
     505  ****/
     506
     507 int get_highest_serial_before_date (long *highest_serial_ptr, long date)
         /* [<][>][^][v][top][bottom][index][help] */
     508 {
     509
     510   char query[MAXCMDLEN];
     511   SQ_result_set_t *result;
     512   SQ_row_t *row;
     513   long highest_serial_last, highest_serial_history;
     514
     515   /* sprintf (query, "SELECT MAX(serials.serial_id) FROM history,serials
     516                         WHERE history.timestamp < %ld
     517                         AND history.object_id = serials.object_id
     518                         AND history.sequence_id = serials.sequence_id ", date); */
     519
     520
     521 /*    sprintf (query, "SELECT MAX(serials.serial_id)
     522                         FROM serials NATURAL LEFT JOIN last NATURAL LEFT JOIN history
     523                         WHERE ((last.timestamp < %ld
     524                                 AND last.object_id = serials.object_id
     525                                 AND last.sequence_id = last.sequence_id)
     526                         OR (history.timestamp < %ld
     527                                 AND history.object_id = serials.object_id
     528                                 AND history.sequence_id = serials.sequence_id))",
     529            date, date);
     530
     531 printf(query);
     532
     533   execute_sql_query(query, &result);
     534   if ( (row = SQ_row_next(result)) != NULL )
     535     {
     536       *highest_serial_ptr = row[0] ? atol((const char *)row[0]) : 0;
     537     }
     538
     539   SQ_free_result(result);
     540
     541 */
     542     sprintf (query, "SELECT MAX(serials.serial_id)
     543                      FROM serials, last
     544                      WHERE (last.object_id = serials.object_id
     545                      AND last.sequence_id = serials.sequence_id
     546                      AND last.timestamp < %ld)", date);
     547
     548   execute_sql_query(query, &result);
     549   if ( (row = SQ_row_next(result)) != NULL )
     550     {
     551       highest_serial_last = row[0] ? atol((const char *)row[0]) : 0;
     552     }
     553
     554   SQ_free_result(result);
     555
     556     sprintf (query, "SELECT MAX(serials.serial_id)
     557                      FROM serials, history
     558                      WHERE (history.object_id = serials.object_id
     559                      AND history.sequence_id = serials.sequence_id
     560                      AND history.timestamp < %ld)", date);
     561
     562   execute_sql_query(query, &result);
     563   if ( (row = SQ_row_next(result)) != NULL )
     564     {
     565       highest_serial_history = row[0] ? atol((const char *)row[0]) : 0;
     566     }
     567
     568   SQ_free_result(result);
     569
     570   if(highest_serial_history>highest_serial_last)
     571           *highest_serial_ptr=highest_serial_history;
     572   else *highest_serial_ptr=highest_serial_last;
     573
     574   return(0);
     575
     576 } /* get_highest_serial_before_date() */
     577
     578
     579
     580 /****
     581  *
     582  * archive_serials_and_history():
     583  * This function contains the core algorithm that manipulates the last,
     584  * history and serials tables and archives them into serials_archive
     585  * and history_archive tables.
     586  *
     587  * Returns number of serials archived
     588  * -1 if error occured
     589  *
     590  ****/
     591
     592 int archive_serials_and_history (long highest_serial)
         /* [<][>][^][v][top][bottom][index][help] */
     593 {
     594
     595   char query[MAXCMDLEN];
     596   SQ_result_set_t *result;
     597   SQ_row_t *row;
     598   long serial, objid, seqid;
     599   int atlast, op;
     600   int nserials=0;
     601   char *tablename;
     602   long timestamp;
     603   /* For crash-recovery test */
     604   int crashtest = CRASHTEST;
     605   long smallest_serial;
     606   time_t now = time(0);
     607
     608
     609   if (crashtest)
     610     {
     611
     612       /*
     613
     614          If you want to run blackbox crash-recovery testing, all you need to do is add
     615          the random-crashing function between the archiving functions that modify
     616          the SQL DB:
     617
     618          if ((crashtest) && (serial == crashing_serial)) do_crash(serial, 0);
     619
     620          and activate the CRASHTEST flag at the top of this file.
     621
     622        */
     623
     624
     625       smallest_serial = get_smallest_serial();
     626       crashing_serial = get_random_number_in_range(smallest_serial, highest_serial, (long)now);
     627       /* crashing_serial = 0; */
     628       if (debug) fprintf (stderr, "Crashing serial: %ld\n",crashing_serial);
     629       code_location = 1;
     630       crash_position = get_random_number_in_range(code_location, MAX_STEPS, (long)now);
     631     }
     632
     633
     634   /* Get the entries for each serial */
     635   /* One word about the "<": Don't use "<=" because if highest_serial
     636      is the CURRENTSERIAL, it causes big problems to UD!
     637      (at least in mirror mode...) */
     638   sprintf (query, "SELECT serials.serial_id, serials.atlast,
     639                         ELT(serials.atlast+1,'history','last','failed_transaction'),
     640                         serials.object_id, serials.sequence_id, serials.operation
     641                         FROM serials
     642                         WHERE serials.serial_id < %ld
     643                         ORDER BY serials.serial_id", highest_serial);
     644   execute_sql_query(query, &result);
     645
     646   /* Loop on every serial */
     647   while ( (row = SQ_row_next(result)) != NULL )
     648     {
     649        nserials++;
     650       /* The lock is inserted here, inside the loop, because it is
     651        * a write lock, which disallows the reading of the table.
     652        * Since one concerned table is "last", the queries would
     653        * be blocked.
     654        * By freeing the lock at the end of every loop, we are assured
     655        * that the reads in queue are executed before a new lock is set.
     656        */
     657
     658       /* Lock (write lock!) relevant tables */
     659       lock_last_history_serial_tables();
     660
     661       /* XXX Add stronger error checking: NULL rows should never happen */
     662       serial = row[0] ? atol((const char *)row[0]) : 0;
     663       atlast = row[1] ? atoi((const char *)row[1]) : IN_UNDEF_TABLE;
     664
     665       if (row[2] == NULL)
     666         {
     667           /* That should never happen! */
     668           fprintf (stderr, "Fatal: No pointer to table\n");
     669           return (-1);
     670         }
     671       else
     672         {
     673           tablename = strdup((const char *)row[2]);
     674         }
     675
     676       objid = atol((const char *)row[3]);
     677       seqid = atol((const char *)row[4]);
     678       op = atol((const char *)row[5]);
     679
     680       /* printf ("Serial: %ld; Atlast: %d; Objid: %ld; Seqid: %ld; Op: %d; Tablename: %s\n",serial, atlast, objid, seqid, op, tablename); */
     681
     682       free(tablename);
     683
     684       /* For crashtests */
     685       code_location = 1;
     686       case_branch = 0;
     687
     688       if (atlast == IN_FAILED_TRANSACTION_TABLE)
     689         {
     690
     691           /* The serial points to a failed transaction */
     692
     693           /* Checkpointing: if recovering, delete from archive */
     694           update_hs_auxiliary_checkpoint(serial, objid, seqid, atlast, CHKP_DELETE_FROM_ARCHIVE);
     695
     696           /* Support for deletion of dummy objects (that happens when a DEL+ADD
     697              updates a dummy object in the DB): the deletion goes to the
     698              failed_transaction table, but we want to put it into the history_archive
     699              with the correct object_id and sequence_id */
     700           if (objid != 0)
     701             {
     702
     703               /* Archive serial with sequence_id = 1 instead of 0 */
     704               archive_serial(serial, objid, seqid+1, op);
     705
     706               /* Archive the object from the failed transaction table */
     707               archive_failed_transaction(serial);
     708
     709               /* Update the object in history_archive with the correct objid
     710                  and seqid = 1 */
     711               update_history_archive_object_id_and_sequence_id (serial, objid, seqid+1);
     712
     713               /* Update prev_serial of the corresponding ADD entry */
     714               if (!update_prev_serial_of_object(objid, seqid+2, serial, "history"))
     715                 { update_prev_serial_of_object(objid, seqid+2, serial, "last"); }
     716
     717             }
     718           else
     719             {
     720
     721               /* Archive serial */
     722               archive_serial(serial, objid, seqid, op);
     723
     724               /* Archive failed transaction */
     725               archive_failed_transaction(serial);
     726
     727             }
     728
     729           /* Checkpointing: if recovering, delete from the live DB - all has been archived */
     730           update_hs_auxiliary_checkpoint(serial, objid, seqid, atlast, CHKP_DELETE_FROM_LIVE);
     731
     732           /* Delete serial */
     733           delete_serial_entry(serial);
     734
     735           /* Delete failed transaction */
     736           delete_failed_transaction_entry(serial);
     737
     738           /* Done */
     739           update_hs_auxiliary_checkpoint(serial, objid, seqid, atlast, CHKP_DONE);
     740
     741         }
     742       else /* atlast == (IN_LAST_TABLE || IN_HISTORY_TABLE) */
     743         {
     744
     745           if (op == OP_DELETE)
     746             {
     747
     748               /* Then it must be in the history */
     749
     750               if (debug) printf ("Deleted serial. Objid: %ld, seqid: %ld, serial: %ld\n",objid, seqid, serial);
     751
     752               /* We need to update the prev_serial of the next element, if there is one...
     753                * This compensates for UPD = DEL + ADD; the ADD is treated with the same
     754                * object_id and sequence_id++ */
     755               if (!update_prev_serial_of_next_object(objid, seqid, serial, "history"))
     756                 update_prev_serial_of_next_object(objid, seqid, serial, "last");
     757
     758               /* Checkpointing: if recovering, delete from archive */
     759               update_hs_auxiliary_checkpoint(serial, objid, seqid, atlast, CHKP_DELETE_FROM_ARCHIVE);
     760
     761               /* Archive serial */
     762               archive_serial(serial, objid, seqid, op);
     763
     764               /* XXX Fixme: no timestamp is archived if this DEL is part of a DEL+ADD .
     765                * This could be solved by fetching the timestamp from the next
     766                * sequence_id (which is the corresponding UPD), if existent.
     767                */
     768
     769               /* Fetch timestamp from the corresponding empty last entry */
     770               /* We need that for deleted objects; the actual entry is in the history,
     771                * but the timestamp of the deletion is in the (otherwise empty) last entry */
     772               timestamp = fetch_timestamp_from_last(objid, seqid);
     773
     774               /* printf ("Timestamp for serial %ld: %ld\n",serial, timestamp); */
     775
     776               /* Archive history:
     777                * we need a special function here because we need to archive
     778                * history.serial as history_archive.prev_serial .
     779                */
     780               copy_deleted_object_into_history_archive(serial, objid, seqid, "history");
     781
     782               /* Update history archive with correct timestamp */
     783               /* XXX We don't really need a function which also updates the seq_id */
     784               update_history_archive_sequence_id_and_timestamp(serial, seqid, timestamp);
     785
     786               /* Checkpointing: if recovering, delete from the live DB - all has been archived */
     787               update_hs_auxiliary_checkpoint(serial, objid, seqid, atlast, CHKP_DELETE_FROM_LIVE);
     788
     789               /* Delete serial */
     790               delete_serial_entry(serial);
     791
     792               /* Delete corresponding empty last entry: it has a seq_id of 0 */
     793               /* It must only do so if the entry to be deleted is not the
     794                  highest object_id */
     795               /* This will have no effect for DEL+ADD operations,
     796                  but no harm is done if left so (sequence_id = 0 only for the empty entries) */
     797               if (objid < highest_objid)
     798                 {
     799                   if (debug) printf ("Deleting empty entry in last table: object_id = %ld\n",objid);
     800                   delete_last_entry(objid, 0);
     801                 }
     802
     803               /* Delete history entry */
     804               delete_history_entry(objid, seqid);
     805
     806               /* Done */
     807               update_hs_auxiliary_checkpoint(serial, objid, seqid, atlast, CHKP_DONE);
     808
     809               /*              if ((crashtest) && (serial == crashing_serial)) die;*/
     810
     811             }
     812           else /* It is an update */
     813             {
     814
     815               if (atlast == IN_LAST_TABLE )
     816                 {
     817
     818                   /* Checkpointing: if recovering, delete from archive */
     819                   update_hs_auxiliary_checkpoint(serial, objid, seqid, atlast, CHKP_DELETE_FROM_ARCHIVE);
     820
     821                   /* Archive serial */
     822                   archive_serial(serial, objid, seqid, op);
     823
     824                   /* Archive last */
     825                   archive_last(serial, objid, seqid);
     826
     827                   /* Update serial element of the entry in last table */
     828                   update_serial_in_last(objid, seqid, serial);
     829
     830                   /* Checkpointing: if recovering, delete from the live DB - all has been archived */
     831                   update_hs_auxiliary_checkpoint(serial, objid, seqid, atlast, CHKP_DELETE_FROM_LIVE_ONLY_SERIAL);
     832
     833                   /* Delete serial */
     834                   delete_serial_entry(serial);
     835
     836                   /* !!!Do not delete the "last" entry!!! */
     837
     838                   /* Done */
     839                   update_hs_auxiliary_checkpoint(serial, objid, seqid, atlast, CHKP_DONE);
     840
     841                 }
     842               else /* atlast == IN_HISTORY_TABLE */
     843                 {
     844
     845                   /* We check for the next object, in order to update
     846                    * its prev_serial. We first look in the history table,
     847                    * then in the last table, otherwise there is no such object
     848                    * => the following update is in fact a deletion...
     849                    */
     850
     851                   if (check_if_next_is_deletion(objid, seqid) == 0)
     852                     {
     853
     854                       /* We are dealing with a last-update-before-deletion */
     855
     856                       /* update_prev_serial_of_next_object() returns the number of
     857                        * affected rows: this shows us if the operation has been successful
     858                        * or not */
     859                       if (!update_prev_serial_of_next_object(objid, seqid, serial, "history"))
     860                         update_prev_serial_of_next_object(objid, seqid, serial, "last");
     861
     862                       /* Checkpointing: if recovering, delete from archive */
     863                       update_hs_auxiliary_checkpoint(serial, objid, seqid, atlast, CHKP_DELETE_FROM_ARCHIVE);
     864
     865                       /* Archive serial */
     866                       archive_serial(serial, objid, seqid, op);
     867
     868                       /* Archive history */
     869                       archive_history(serial, objid, seqid);
     870
     871                       /* Checkpointing: if recovering, delete from the live DB - all has been archived */
     872                       update_hs_auxiliary_checkpoint(serial, objid, seqid, atlast, CHKP_DELETE_FROM_LIVE);
     873
     874                       /* Delete serial */
     875                       delete_serial_entry(serial);
     876
     877                       /* Delete history */
     878                       delete_history_entry(objid, seqid);
     879
     880                       /* Done */
     881                       update_hs_auxiliary_checkpoint(serial, objid, seqid, atlast, CHKP_DONE);
     882
     883                     }
     884                   else /* This is the one always executed */
     885                     {
     886
     887                       case_branch = 6;
     888
     889                       /* Checkpointing: if recovering, delete from archive */
     890                       update_hs_auxiliary_checkpoint(serial, objid, seqid, atlast, CHKP_DELETE_FROM_ARCHIVE);
     891
     892                       /* Archive serial */
     893                       archive_serial(serial, objid, seqid, op);
     894
     895                       /* Archive history */
     896                       archive_history(serial, objid, seqid);
     897
     898                       /* Update serial in -current- history entry */
     899                       update_serial_in_history(objid, seqid, serial);
     900
     901                       /* Checkpointing: if recovering, delete from the live DB - all has been archived */
     902                       update_hs_auxiliary_checkpoint(serial, objid, seqid, atlast, CHKP_DELETE_FROM_LIVE_ONLY_SERIAL);
     903
     904                       /* Delete serial */
     905                       delete_serial_entry(serial);
     906
     907                       /* Do not delete current history entry! It will be needed
     908                          by the deleting serial */
     909
     910                       /* Done */
     911                       update_hs_auxiliary_checkpoint(serial, objid, seqid, atlast, CHKP_DONE);
     912
     913                     }
     914
     915                 }
     916
     917             }
     918
     919         }
     920
     921       /* Unlock relevant tables */
     922       unlock_all_tables();
     923
     924     }
     925
     926   SQ_free_result(result);
     927
     928   return(nserials);
     929
     930 } /* archive_serials_and_history() */
     931
     932
     933
     934 /****
     935  *
     936  * fetch_timestamp_from_last()
     937  * Get the timestamp of a specific (object_id, sequence_id)
     938  * from the last table
     939  *
     940  ****/
     941
     942 long fetch_timestamp_from_last (long obj_id, long seq_id)
         /* [<][>][^][v][top][bottom][index][help] */
     943 {
     944
     945   long timestamp = 0;
     946   char query[MAXCMDLEN];
     947   SQ_result_set_t *result;
     948   SQ_row_t *row;
     949
     950   sprintf (query, "SELECT timestamp FROM last WHERE object_id = %ld AND sequence_id = %ld",
     951            obj_id, seq_id);
     952
     953   execute_sql_query(query, &result);
     954   if ( (row = SQ_row_next(result)) != NULL)
     955     timestamp = atol((const char *)row[0]);
     956
     957   SQ_free_result(result);
     958
     959   return(timestamp);
     960
     961 } /* fetch_timestamp_from_last() */
     962
     963
     964
     965 /****
     966  *
     967  * get_highest_object_id()
     968  * Get the highest object_id in the last table.
     969  *
     970  ****/
     971
     972 long get_highest_object_id()
         /* [<][>][^][v][top][bottom][index][help] */
     973 {
     974
     975   long highest_objid = 0;
     976   char query[MAXCMDLEN];
     977   SQ_result_set_t *result;
     978   SQ_row_t *row;
     979
     980   sprintf (query, "SELECT max(object_id) FROM last");
     981
     982   execute_sql_query(query, &result);
     983   if ( (row = SQ_row_next(result)) != NULL)
     984     highest_objid = atol((const char *)row[0]);
     985
     986   SQ_free_result(result);
     987
     988   return(highest_objid);
     989
     990
     991 } /* get_highest_object_id() */
     992
     993
     994 /****
     995  *
     996  * check_if_next_is_deletion()
     997  * This functions checks if there is a row in the serials
     998  * table with same obj_id and seq_id, but a delete operation.
     999  * This would mean that we are dealing with a last-update-before-deletion.
    1000  *
    1001  ****/
    1002
    1003 int check_if_next_is_deletion (long obj_id, long seq_id)
         /* [<][>][^][v][top][bottom][index][help] */
    1004 {
    1005
    1006   char query[MAXCMDLEN];
    1007   SQ_result_set_t *result;
    1008   SQ_row_t *row;
    1009   long serial = 0;
    1010
    1011   sprintf (query, "SELECT serial_id, atlast FROM serials
    1012                         WHERE object_id = %ld AND sequence_id = %ld AND operation = %d",
    1013            obj_id, seq_id, OP_DELETE);
    1014
    1015   execute_sql_query(query, &result);
    1016   if ( (row = SQ_row_next(result)) != NULL)
    1017     serial = atol((const char *)row[0]);
    1018
    1019   SQ_free_result(result);
    1020
    1021   return(serial);
    1022
    1023 } /* check_if_next_is_deletion() */
    1024
    1025
    1026
    1027
    1028
    1029 /*** Table creation ***/
    1030
    1031 /****
    1032  *
    1033  * create_archive_tables():
    1034  * Create tables for history and serials archives
    1035  * if they do not exist
    1036  *
    1037  ****/
    1038
    1039 int create_archive_tables()
         /* [<][>][^][v][top][bottom][index][help] */
    1040 {
    1041
    1042   char cmd[MAXCMDLEN];
    1043
    1044   sprintf (cmd,
    1045            "CREATE TABLE history_archive (
    1046                 object_id int(10) unsigned DEFAULT '0' NOT NULL,
    1047                 sequence_id int(10) unsigned DEFAULT '0' NOT NULL,
    1048                 serial int(11) DEFAULT '0' NOT NULL,
    1049                 prev_serial int(11) DEFAULT '0' NOT NULL,
    1050                 timestamp int(10) unsigned DEFAULT '0' NOT NULL,
    1051                 object_type tinyint(3) unsigned DEFAULT '0' NOT NULL,
    1052                 object longblob NOT NULL,
    1053                 pkey varchar(64) default '' NOT NULL,
    1054                 PRIMARY KEY (object_id,sequence_id,serial),
    1055                 INDEX (serial)
    1056                 );");
    1057   create_table(cmd);
    1058
    1059
    1060   sprintf (cmd,
    1061            "CREATE TABLE serials_archive (
    1062                 serial_id int(11) DEFAULT '0' NOT NULL,
    1063                 object_id int(10) unsigned DEFAULT '0' NOT NULL,
    1064                 sequence_id int(10) unsigned DEFAULT '0' NOT NULL,
    1065                 operation tinyint(4) unsigned DEFAULT '0' NOT NULL,
    1066                 PRIMARY KEY (serial_id)
    1067                 );");
    1068   create_table(cmd);
    1069
    1070   return(0);
    1071
    1072 } /* create_archive_tables() */
    1073
    1074
    1075
    1076 /****
    1077  *
    1078  * create_table()
    1079  * This function wraps a table creation (which must be already
    1080  * specified in cmd), and only issues a warning if the table
    1081  * already exists.
    1082  *
    1083  ****/
    1084
    1085 int create_table (const char *cmd)
         /* [<][>][^][v][top][bottom][index][help] */
    1086 {
    1087
    1088   int state;
    1089
    1090   state = SQ_execute_query(connection, cmd, NULL);
    1091   if (state != 0)
    1092     {
    1093       /* XXX is ER_TABLE_EXISTS_ERROR mysql-bounded? */
    1094       if (SQ_errno(connection) == ER_TABLE_EXISTS_ERROR)
    1095         {
    1096           /* Don't die if a table already exists */
    1097           fprintf (stderr,"Warning: %s\n",SQ_error(connection));
    1098           return (state);
    1099         }
    1100       else
    1101         {
    1102           fprintf (stderr,"Fatal: %s\n",SQ_error(connection));
    1103           die;
    1104         }
    1105     }
    1106
    1107   return(0);
    1108
    1109 } /* create_table() */
    1110
    1111
    1112
    1113
    1114
    1115 /*** Object update ***/
    1116
    1117 /****
    1118  *
    1119  * update_history_archive_sequence_id_and_timestamp()
    1120  *
    1121  ****/
    1122
    1123 int update_history_archive_sequence_id_and_timestamp (long ser_id, long new_seq_id, long new_timestamp)
         /* [<][>][^][v][top][bottom][index][help] */
    1124 {
    1125
    1126   char cmd[MAXCMDLEN];
    1127
    1128   sprintf (cmd,"UPDATE history_archive
    1129                 SET timestamp = %ld, sequence_id = %ld
    1130                 WHERE serial = %ld",
    1131            new_timestamp, new_seq_id, ser_id);
    1132
    1133   return (execute_sql_command(cmd));
    1134
    1135 } /* update_history_archive_sequence_id_and_timestamp() */
    1136
    1137
    1138
    1139 /****
    1140  *
    1141  * update_history_archive_object_id_and_sequence_id()
    1142  *
    1143  ****/
    1144
    1145 int update_history_archive_object_id_and_sequence_id (long ser_id, long new_obj_id, long new_seq_id)
         /* [<][>][^][v][top][bottom][index][help] */
    1146 {
    1147
    1148   char cmd[MAXCMDLEN];
    1149
    1150   sprintf (cmd,"UPDATE history_archive
    1151                 SET object_id = %ld, sequence_id = %ld
    1152                 WHERE serial = %ld",
    1153            new_obj_id, new_seq_id, ser_id);
    1154
    1155   return (execute_sql_command(cmd));
    1156
    1157 } /* update_history_archive_object_id_and_sequence_id() */
    1158
    1159
    1160
    1161 /****
    1162  *
    1163  * update_prev_serial_of_object()
    1164  *
    1165  ****/
    1166
    1167 int update_prev_serial_of_object (long obj_id, long seq_id, long prev_ser, const char *tablename)
         /* [<][>][^][v][top][bottom][index][help] */
    1168 {
    1169
    1170   char cmd[MAXCMDLEN];
    1171
    1172   sprintf (cmd,"UPDATE %s
    1173                 SET prev_serial = %ld
    1174                 WHERE object_id = %ld
    1175                 AND sequence_id = %ld",
    1176            tablename, prev_ser, obj_id, seq_id);
    1177
    1178   return(execute_sql_command(cmd));
    1179
    1180 } /* update_prev_serial_of_object() */
    1181
    1182
    1183
    1184 /****
    1185  *
    1186  * update_serial_of_object()
    1187  *
    1188  ****/
    1189
    1190 int update_serial_of_object (long obj_id, long seq_id, long ser_id, const char *tablename)
         /* [<][>][^][v][top][bottom][index][help] */
    1191 {
    1192
    1193   char cmd[MAXCMDLEN];
    1194
    1195   sprintf (cmd,"UPDATE %s
    1196                 SET serial = %ld
    1197                 WHERE object_id = %ld
    1198                 AND sequence_id = %ld",
    1199            tablename, ser_id, obj_id, seq_id);
    1200
    1201   return(execute_sql_command(cmd));
    1202
    1203 } /* update_serial_of_object() */
    1204
    1205
    1206
    1207
    1208
    1209 /*** Object archiving ***/
    1210
    1211 /****
    1212  *
    1213  * archive_serial()
    1214  *
    1215  ****/
    1216
    1217 int archive_serial (long ser_id, long obj_id, long seq_id, int op)
         /* [<][>][^][v][top][bottom][index][help] */
    1218 {
    1219
    1220   /* Put the given values into the serials_archive table */
    1221
    1222   char cmd[MAXCMDLEN];
    1223
    1224   sprintf (cmd, "INSERT INTO serials_archive (serial_id, object_id, sequence_id, operation)
    1225                  VALUES (%ld, %ld, %ld, %d)",
    1226            ser_id, obj_id, seq_id, op);
    1227
    1228   return (execute_sql_command(cmd));
    1229
    1230 } /* archive_serial() */
    1231
    1232
    1233
    1234 /****
    1235  *
    1236  * archive_failed_transaction()
    1237  *
    1238  ****/
    1239
    1240 int archive_failed_transaction (long ser_id)
         /* [<][>][^][v][top][bottom][index][help] */
    1241 {
    1242
    1243   char cmd[MAXCMDLEN];
    1244
    1245   sprintf (cmd,"INSERT INTO history_archive (serial, timestamp, object)
    1246                 SELECT failed_transaction.serial_id, failed_transaction.timestamp, failed_transaction.object
    1247                 FROM failed_transaction
    1248                 WHERE failed_transaction.serial_id = %ld",
    1249            ser_id);
    1250
    1251   return (execute_sql_command(cmd));
    1252
    1253 } /* archive_failed_transaction() */
    1254
    1255
    1256
    1257 /****
    1258  *
    1259  * copy_into_history_archive()
    1260  * Generic function that works both for last and history tables
    1261  *
    1262  ****/
    1263
    1264 int copy_into_history_archive (long ser_id, long obj_id, long seq_id, const char *tablename)
         /* [<][>][^][v][top][bottom][index][help] */
    1265 {
    1266
    1267   char cmd[MAXCMDLEN];
    1268
    1269   sprintf (cmd,"INSERT INTO history_archive (object_id, sequence_id, serial, prev_serial, timestamp, object_type, object, pkey)
    1270                 SELECT serials.object_id, serials.sequence_id, serials.serial_id, %s.prev_serial, %s.timestamp, %s.object_type, %s.object, %s.pkey
    1271                 FROM serials,%s
    1272                 WHERE serials.serial_id = %ld
    1273                 AND %s.object_id = %ld
    1274                 AND %s.sequence_id = %ld",
    1275            tablename, tablename, tablename, tablename, tablename, tablename, ser_id, tablename, obj_id, tablename, seq_id);
    1276
    1277   return (execute_sql_command(cmd));
    1278
    1279 } /* copy_into_history_archive() */
    1280
    1281
    1282
    1283 /****
    1284  *
    1285  * copy_deleted_object_into_history_archive()
    1286  * The difference here is that we archive the history.serial as history_archive.prev_serial .
    1287  * This is only needed for history objects corresponding to OP_DELETE,
    1288  * where the row actually corresponds to the last update before the deletion.
    1289  *
    1290  ****/
    1291
    1292 int copy_deleted_object_into_history_archive (long ser_id, long obj_id, long seq_id, const char *tablename)
         /* [<][>][^][v][top][bottom][index][help] */
    1293 {
    1294
    1295   char cmd[MAXCMDLEN];
    1296   int affected_rows;
    1297
    1298   sprintf (cmd,"INSERT INTO history_archive (object_id, sequence_id, serial, prev_serial, timestamp, object_type, object, pkey)
    1299                 SELECT serials.object_id, serials.sequence_id, serials.serial_id, %s.serial, %s.timestamp, %s.object_type, %s.object, %s.pkey
    1300                 FROM serials,%s
    1301                 WHERE serials.serial_id = %ld
    1302                 AND %s.object_id = %ld
    1303                 AND %s.sequence_id = %ld",
    1304            tablename, tablename, tablename, tablename, tablename, tablename, ser_id, tablename, obj_id, tablename, seq_id);
    1305
    1306   affected_rows = execute_sql_command(cmd);
    1307   if (debug) printf ("copy_deleted_object_into_history_archive (%ld, %ld, %ld, %s): affected rows %d\n",ser_id,obj_id,seq_id,tablename,affected_rows);
    1308   return (affected_rows);
    1309
    1310 } /* copy_deleted_object_into_history_archive() */
    1311
    1312
    1313
    1314
    1315
    1316 /*** Object deletion ***/
    1317
    1318 /****
    1319  *
    1320  * delete_serial_entry()
    1321  *
    1322  ****/
    1323
    1324 int delete_serial_entry (long ser_id)
         /* [<][>][^][v][top][bottom][index][help] */
    1325 {
    1326
    1327   char cmd[MAXCMDLEN];
    1328
    1329   sprintf (cmd, "DELETE FROM serials WHERE serial_id = %ld", ser_id);
    1330
    1331   return (execute_sql_command(cmd));
    1332
    1333 } /* delete_serial_entry() */
    1334
    1335
    1336
    1337 /****
    1338  *
    1339  * delete_failed_transaction_entry()
    1340  *
    1341  ****/
    1342
    1343 int delete_failed_transaction_entry (long ser_id)
         /* [<][>][^][v][top][bottom][index][help] */
    1344 {
    1345
    1346   char cmd[MAXCMDLEN];
    1347
    1348   sprintf (cmd, "DELETE FROM failed_transaction WHERE serial_id = %ld",ser_id);
    1349
    1350   return (execute_sql_command(cmd));
    1351
    1352 } /* delete_failed_transaction_entry() */
    1353
    1354
    1355
    1356 /****
    1357  *
    1358  * delete_entry_from_object_table()
    1359  * Works both for last and history tables
    1360  *
    1361  ****/
    1362
    1363 int delete_entry_from_object_table (long obj_id, long seq_id, const char* tablename)
         /* [<][>][^][v][top][bottom][index][help] */
    1364 {
    1365
    1366   char cmd[MAXCMDLEN];
    1367   int affected_rows;
    1368
    1369   if (debug) printf ("Deleting %s entry. Objid: %ld, seqid: %ld\n",tablename, obj_id, seq_id);
    1370
    1371   sprintf (cmd, "DELETE FROM %s WHERE object_id = %ld AND sequence_id = %ld",
    1372            tablename, obj_id, seq_id);
    1373
    1374   affected_rows = execute_sql_command(cmd);
    1375   if (debug) printf ("delete_entry_from_object_table (%ld, %ld, %s): affected rows %d\n",obj_id,seq_id,tablename,affected_rows);
    1376   return (affected_rows);
    1377
    1378 } /* delete_entry_from_object_table() */
    1379
    1380
    1381
    1382 /****
    1383  *
    1384  * delete_archived_objects()
    1385  *
    1386  ****/
    1387
    1388 int delete_archived_objects(long ser_id)
         /* [<][>][^][v][top][bottom][index][help] */
    1389 {
    1390
    1391   delete_serial_archive_entry(ser_id);
    1392   delete_history_archive_entry(ser_id);
    1393
    1394   return(0);
    1395
    1396 } /* delete_archived_object() */
    1397
    1398
    1399 /****
    1400  *
    1401  * delete_serial_archive_entry()
    1402  *
    1403  ****/
    1404
    1405 int delete_serial_archive_entry (long ser_id)
         /* [<][>][^][v][top][bottom][index][help] */
    1406 {
    1407
    1408   char cmd[MAXCMDLEN];
    1409
    1410   sprintf (cmd, "DELETE FROM serials_archive WHERE serial_id = %ld", ser_id);
    1411
    1412   return (execute_sql_command(cmd));
    1413
    1414 } /* delete_serial_archive_entry() */
    1415
    1416
    1417
    1418 /****
    1419  *
    1420  * delete_history_archive_entry()
    1421  *
    1422  ****/
    1423
    1424 int delete_history_archive_entry (long ser_id)
         /* [<][>][^][v][top][bottom][index][help] */
    1425 {
    1426
    1427   char cmd[MAXCMDLEN];
    1428
    1429   sprintf (cmd, "DELETE FROM history_archive WHERE serial = %ld", ser_id);
    1430
    1431   return (execute_sql_command(cmd));
    1432
    1433 } /* delete_history_archive_entry() */
    1434
    1435
    1436
    1437
    1438
    1439
    1440 /*** Handling of older, unreferenced history entries ***/
    1441
    1442 /****
    1443  *
    1444  * find_unreferenced_history_entries()
    1445  * Deal with very old history entries, those that are not referenced by any serial,
    1446  * due to a previous history/serial cleanup.
    1447  * These are entries which had been archived when they were in the "last" table,
    1448  * and have in the meanwhile been updated. We have to:
    1449  *    - Update prev_serial of their next object
    1450  *    - Delete them: this is done by delete_unreferenced_history_entries()
    1451  *
    1452  ****/
    1453
    1454 tblObjList *find_unreferenced_history_entries(long date)
         /* [<][>][^][v][top][bottom][index][help] */
    1455 {
    1456
    1457   char query[MAXCMDLEN];
    1458   SQ_result_set_t *result;
    1459   SQ_row_t *row;
    1460   long objid, seqid, serid;
    1461
    1462   tblObjList *curListElmt = NULL;
    1463   tblObjList *firstListElmt = NULL;
    1464   tblObjList *tmpList = NULL;
    1465
    1466   /* Find object_id, sequence_id of unreferenced history entries
    1467    * This query returns all history entries which do not have a corresponding
    1468    * (object_id, serial_id) in the serials table
    1469    */
    1470   /* XXX Bug! This will find (and then remove) objects that would be
    1471    * needed in the future by deletions. */
    1472
    1473   sprintf (query, "SELECT history.object_id, history.sequence_id, history.serial
    1474                         FROM history LEFT JOIN serials
    1475                         ON history.serial = serials.serial_id
    1476                         WHERE serials.serial_id is NULL
    1477                         AND history.serial != 0
    1478                         AND history.timestamp < %ld", date);
    1479   execute_sql_query(query, &result);
    1480
    1481   /* Foreach entry: */
    1482   while ( (row = SQ_row_next(result)) != NULL )
    1483     {
    1484
    1485       /* Lock tables in writing... */
    1486       /* XXX We don't need to do it, we are not deleting the objects here! */
    1487       /* lock_last_history_serial_tables(); */
    1488
    1489       /* XXX Error checking missing... */
    1490       objid = atol((const char *)row[0]);
    1491       seqid = atol((const char *)row[1]);
    1492       serid = atol((const char *)row[2]);
    1493
    1494       /* Update prev_serial of the same object with next sequence_id */
    1495       if (!update_prev_serial_of_next_object(objid, seqid, serid, "history"))
    1496         { update_prev_serial_of_next_object(objid, seqid, serid, "last"); }
    1497
    1498       /* Delete the entry */
    1499       if (debug) printf ("I am deleting this entry: %ld, %ld\n",objid, seqid);
    1500
    1501       /* Don't delete the history entries directly! This will cause problems
    1502          if the next serial is a deletion */
    1503       /* Instead, add it in a list that will be batch-deleted at the end */
    1504       /* PushTblObjList (objid, seqid, curListElmt); */
    1505
    1506       tmpList = (tblObjList *)malloc(sizeof(tblObjList));
    1507       tmpList->objid = objid;
    1508       tmpList->seqid = seqid;
    1509       tmpList->next = NULL;
    1510
    1511       if (firstListElmt == NULL)
    1512         {
    1513           firstListElmt = tmpList;
    1514           curListElmt = tmpList;
    1515         }
    1516       else
    1517         {
    1518           curListElmt->next = tmpList;
    1519           curListElmt = curListElmt->next;
    1520         }
    1521
    1522       /* Unlock tables... */
    1523       /* unlock_all_tables(); */
    1524
    1525     }
    1526
    1527   /* printf ("Elements to be deleted:\n");
    1528      for (curListElmt = firstListElmt; curListElmt != NULL; curListElmt = curListElmt->next)
    1529      {
    1530      printf ("objid: %ld, seqid: %ld\n", curListElmt->objid, curListElmt->seqid);
    1531      } */
    1532
    1533   SQ_free_result(result);
    1534
    1535   return (firstListElmt);
    1536
    1537 } /* find_unreferenced_history_entries() */
    1538
    1539
    1540 /****
    1541  *
    1542  * delete_unreferenced_history_entries()
    1543  *
    1544  ****/
    1545
    1546 int delete_unreferenced_history_entries(tblObjList *objectsToDelete)
         /* [<][>][^][v][top][bottom][index][help] */
    1547 {
    1548
    1549   tblObjList *tmpObj;
    1550
    1551   if (debug) printf ("Elements to be deleted:\n");
    1552   for (tmpObj = objectsToDelete; tmpObj != NULL; tmpObj = tmpObj->next)
    1553     {
    1554       lock_last_history_serial_tables();
    1555       if (debug) printf ("objid: %ld, seqid: %ld\n", tmpObj->objid, tmpObj->seqid);
    1556       delete_history_entry(tmpObj->objid, tmpObj->seqid);
    1557       unlock_all_tables();
    1558     }
    1559
    1560   return(0);
    1561
    1562 } /* delete_unreferenced_history_entries() */
    1563
    1564
    1565
    1566 /****
    1567  *
    1568  * PushTblObjList()
    1569  *
    1570  ****/
    1571
    1572 /* XXX Fix this function! It is currently not used. */
    1573
    1574 int PushTblObjList(long objid, long seqid, tblObjList *curListElmt)
         /* [<][>][^][v][top][bottom][index][help] */
    1575 {
    1576
    1577   tblObjList *tmpList;
    1578
    1579   tmpList = (tblObjList *)malloc(sizeof(tblObjList));
    1580   tmpList->objid = objid;
    1581   tmpList->seqid = seqid;
    1582   tmpList->next = NULL;
    1583
    1584   if (curListElmt == NULL)
    1585     {
    1586       curListElmt = tmpList;
    1587     }
    1588   else
    1589     {
    1590       curListElmt->next = tmpList;
    1591       /* curListElmt = tmpList; */
    1592     }
    1593
    1594   if (debug) printf ("Inside PushTblObjList: %ld, %ld\n", curListElmt->objid, curListElmt->seqid);
    1595
    1596   return(0);
    1597
    1598 } /* PushTblObjList() */
    1599
    1600
    1601
    1602
    1603
    1604 /*** Handling of dummy objects ***/
    1605
    1606 /****
    1607  *
    1608  * delete_dummy_history_objects()
    1609  * Deletes from history all "dummy" objects. They should not be
    1610  * archived, being only a tweak of the software to allow mirroring
    1611  *
    1612  ****/
    1613
    1614 int delete_dummy_history_objects()
         /* [<][>][^][v][top][bottom][index][help] */
    1615 {
    1616
    1617   char cmd[MAXCMDLEN];
    1618   int rc;
    1619
    1620   sprintf (cmd,"DELETE FROM history WHERE object_type = %d",OBJ_TYPE_DUMMY);
    1621
    1622   lock_last_history_serial_tables();
    1623   rc = execute_sql_command(cmd);
    1624   unlock_all_tables();
    1625
    1626   printf ("%d dummy history rows deleted.\n", rc);
    1627
    1628   return(rc);
    1629
    1630 } /* delete_dummy_history_objects() */
    1631
    1632
    1633
    1634
    1635
    1636 /*** Interactions with SQL DB ***/
    1637
    1638 /****
    1639  *
    1640  * lock_last_history_serial_tables()
    1641  *
    1642  ****/
    1643
    1644 int lock_last_history_serial_tables()
         /* [<][>][^][v][top][bottom][index][help] */
    1645 {
    1646
    1647   char cmd[MAXCMDLEN];
    1648
    1649   /* No real choice - we must lock the tables in write mode */
    1650
    1651   sprintf (cmd, "LOCK TABLES last WRITE, history WRITE, failed_transaction WRITE, serials WRITE, serials_archive WRITE, history_archive WRITE, hs_auxiliary WRITE");
    1652
    1653   return (execute_sql_command(cmd));
    1654
    1655 } /* lock_last_history_serial_tables() */
    1656
    1657
    1658
    1659 /****
    1660  *
    1661  * unlock_all_tables()
    1662  *
    1663  ****/
    1664
    1665 int unlock_all_tables()
         /* [<][>][^][v][top][bottom][index][help] */
    1666 {
    1667
    1668   char cmd[MAXCMDLEN];
    1669
    1670   sprintf (cmd, "UNLOCK TABLES");
    1671
    1672   return (execute_sql_command(cmd));
    1673
    1674 } /* unlock_all_tables() */
    1675
    1676
    1677
    1678 /****
    1679  *
    1680  * optimize_sql_table()
    1681  *
    1682  ****/
    1683
    1684 int optimize_sql_table(const char* tablename)
         /* [<][>][^][v][top][bottom][index][help] */
    1685 {
    1686
    1687   char cmd[MAXCMDLEN];
    1688
    1689   sprintf (cmd, "OPTIMIZE TABLE %s", tablename);
    1690
    1691   return (execute_sql_command(cmd));
    1692
    1693 } /* optimize_sql_table() */
    1694
    1695
    1696
    1697
    1698
    1699 /*** SQL interfaces ***/
    1700
    1701 /****
    1702  *
    1703  * execute_sql_query()
    1704  * Returns a result in result_ptr;
    1705  * the return code is the state.
    1706  *
    1707  ****/
    1708
    1709 int execute_sql_query (const char *query, SQ_result_set_t **result_ptr)
         /* [<][>][^][v][top][bottom][index][help] */
    1710 {
    1711
    1712   int state;
    1713
    1714   state = SQ_execute_query(connection, query, result_ptr);
    1715   if (state != 0)
    1716     {
    1717       fprintf (stderr, "Fatal:\n Offending query: %s\n Error: %s\n",query,SQ_error(connection));
    1718       die;
    1719     }
    1720
    1721   return(state);
    1722
    1723 } /* execute_sql_query() */
    1724
    1725
    1726
    1727 /****
    1728  *
    1729  * execute_sql_command()
    1730  * Does not return any result;
    1731  * the return code is the number of affected rows.
    1732  *
    1733  ****/
    1734
    1735 int execute_sql_command (const char *cmd)
         /* [<][>][^][v][top][bottom][index][help] */
    1736 {
    1737
    1738   int state;
    1739
    1740   state = SQ_execute_query(connection, cmd, NULL);
    1741   if (state != 0)
    1742     {
    1743       fprintf (stderr, "Fatal:\n Offending command: %s\n Error: %s\n",cmd,SQ_error(connection));
    1744       die;
    1745     }
    1746
    1747   return(SQ_get_affected_rows(connection));
    1748
    1749 } /* execute_sql_command() */
    1750
    1751
    1752
    1753
    1754
    1755 /*** Checkpointing ***/
    1756
    1757 /****
    1758  *
    1759  * create_auxiliary_table()
    1760  * This auxiliary table will record some checkpointing
    1761  * data, in order to recover from crashes
    1762  * and to help with the clenup of the older history tables
    1763  *
    1764  ****/
    1765
    1766 int create_auxiliary_table()
         /* [<][>][^][v][top][bottom][index][help] */
    1767 {
    1768
    1769   int state;
    1770   char cmd[MAXCMDLEN];
    1771
    1772   sprintf (cmd,"CREATE TABLE hs_auxiliary (
    1773                 serial int(11) DEFAULT '0' NOT NULL,
    1774                 object_id int(10) unsigned DEFAULT '0' NOT NULL,
    1775                 sequence_id int(10) unsigned DEFAULT '0' NOT NULL,
    1776                 atlast tinyint(4) unsigned DEFAULT '0' NOT NULL,
    1777                 checkpoint tinyint(4) unsigned DEFAULT '0' NOT NULL,
    1778                 PRIMARY KEY (serial)
    1779                 );");
    1780   state = create_table(cmd);
    1781   if (state == 0) /* state != 0 only if the table already exists - other errors make the program die */
    1782     {
    1783       fprintf (stderr,"Table created. Inserting dummy objects.\n");
    1784       /* We also need to create a dummy row if the table had not been created */
    1785       sprintf (cmd,"INSERT INTO hs_auxiliary VALUES (0, 0, 0, 0, 0)");
    1786       execute_sql_command(cmd);
    1787     }
    1788
    1789   return(0);
    1790
    1791 } /* create_auxiliary_table() */
    1792
    1793
    1794
    1795 /****
    1796  *
    1797  * reset_auxiliary_table()
    1798  *
    1799  ****/
    1800
    1801 int reset_auxiliary_table()
         /* [<][>][^][v][top][bottom][index][help] */
    1802 {
    1803
    1804   char cmd[MAXCMDLEN];
    1805
    1806   sprintf (cmd,"UPDATE hs_auxiliary SET
    1807                 serial = 0, object_id = 0, sequence_id = 0, atlast = 0, checkpoint = 0");
    1808
    1809   return(execute_sql_command(cmd));
    1810
    1811 } /* reset_auxiliary_table() */
    1812
    1813
    1814
    1815 /****
    1816  *
    1817  * drop_auxiliary_table()
    1818  *
    1819  ****/
    1820
    1821 int drop_auxiliary_table()
         /* [<][>][^][v][top][bottom][index][help] */
    1822 {
    1823
    1824   char cmd[MAXCMDLEN];
    1825
    1826   sprintf (cmd,"DROP TABLE IF EXISTS hs_auxiliary");
    1827   return(execute_sql_command(cmd));
    1828
    1829 } /* drop_auxiliary_table() */
    1830
    1831
    1832
    1833 /****
    1834  *
    1835  * update_hs_auxiliary_checkpoint()
    1836  *
    1837  ****/
    1838
    1839 int update_hs_auxiliary_checkpoint(long ser_id, long obj_id, long seq_id, int atlast, int checkpoint)
         /* [<][>][^][v][top][bottom][index][help] */
    1840 {
    1841
    1842   char cmd[MAXCMDLEN];
    1843
    1844   sprintf (cmd,"UPDATE hs_auxiliary
    1845                 SET serial = %ld,
    1846                 object_id = %ld,
    1847                 sequence_id = %ld,
    1848                 atlast = %d,
    1849                 checkpoint = %d",
    1850            ser_id, obj_id, seq_id, atlast, checkpoint);
    1851
    1852   return (execute_sql_command(cmd));
    1853
    1854 } /* update_hs_auxiliary_checkpoint() */
    1855
    1856
    1857
    1858 /****
    1859  *
    1860  * crash_recovery()
    1861  * Check if last time we crashed; if so, try to recover.
    1862  *
    1863  ****/
    1864
    1865 int crash_recovery()
         /* [<][>][^][v][top][bottom][index][help] */
    1866 {
    1867
    1868   char query[MAXCMDLEN];
    1869   SQ_result_set_t *result;
    1870   SQ_row_t *row;
    1871   long serial, objid, seqid;
    1872   int atlast, chkp;
    1873
    1874
    1875   if (!exists_checkpointing_table())
    1876     {
    1877       /* The checkpointing table does not exist, there was no crash!
    1878          Create the table and back to work. */
    1879       fprintf (stderr, "No auxiliary table found. Creating it...\n");
    1880       create_auxiliary_table();
    1881       return(0);
    1882     }
    1883
    1884   /* Otherwise, let's start recovering... */
    1885   fprintf (stderr, "The auxiliary table exists! Start recovering...\n");
    1886
    1887   sprintf(query, "SELECT serial, object_id, sequence_id, atlast, checkpoint FROM hs_auxiliary");
    1888
    1889   execute_sql_query(query, &result);
    1890   if ( (row = SQ_row_next(result)) != NULL )
    1891     {
    1892       serial = atol((const char *)row[0]);
    1893       objid = atol((const char *)row[1]);
    1894       seqid = atol((const char *)row[2]);
    1895       atlast = atoi((const char *)row[3]);
    1896       chkp = atoi((const char *)row[4]);
    1897       if (debug) fprintf (stderr,"DEBUG: Recovering from crash.\n It happened on serial %ld, object_id %ld, sequence_id %ld, table %s, checkpoint %d\n",
    1898               serial, objid, seqid, atlast_table[atlast], chkp);
    1899     }
    1900   else
    1901     {
    1902       /* The table is empty! Weird, but return */
    1903       fprintf (stderr, "The checkpointing table exists but is empty!\n");
    1904       drop_auxiliary_table();
    1905       return(0);
    1906     }
    1907
    1908   /* Recover depending on what the checkpointing is */
    1909   switch(chkp)
    1910     {
    1911     case CHKP_DELETE_FROM_ARCHIVE:
    1912       /* Delete all the archived objects corresponding to that serial */
    1913       if (debug) fprintf (stderr, "DEBUG: Deleting archived objects for serial %ld\n",serial);
    1914       delete_archived_objects(serial);
    1915       break;
    1916     case CHKP_DELETE_FROM_LIVE:
    1917       /* In this case, we have to delete the corresponding objects in the live DB */
    1918       if (debug) fprintf (stderr, "DEBUG: Deleting serial entry %ld\n",serial);
    1919       delete_serial_entry(serial);
    1920       if (atlast == IN_FAILED_TRANSACTION_TABLE)
    1921         {
    1922           if (debug) fprintf (stderr, "DEBUG: Deleting failed transaction entry for serial %ld\n",serial);
    1923           delete_failed_transaction_entry(serial);
    1924         }
    1925       else if (atlast != IN_LAST_TABLE) /* Should never happen, double-check */
    1926         /* (It can actually only be in the history table) */
    1927         {
    1928           if (debug) fprintf (stderr, "DEBUG: Deleting history entry for serial %ld\n",serial);
    1929           delete_entry_from_object_table(objid, seqid, atlast_table[atlast]);
    1930         }
    1931       else
    1932         fprintf (stderr,"WARNING! Attempt to delete object from last table in crash-recovery\n");
    1933       break;
    1934     case CHKP_DELETE_FROM_LIVE_ONLY_SERIAL:
    1935       if (debug) fprintf (stderr, "DEBUG: Deleting serial entry %ld\n",serial);
    1936       delete_serial_entry(serial);
    1937       break;
    1938     case CHKP_NOOP:
    1939     case CHKP_DONE:
    1940     default:
    1941       /* Do nothing */
    1942       break;
    1943     }
    1944
    1945   reset_auxiliary_table();
    1946
    1947   SQ_free_result(result);
    1948
    1949   return(0);
    1950
    1951 } /* crash_recovery() */
    1952
    1953
    1954
    1955 /****
    1956  *
    1957  * exists_checkpointing_table()
    1958  * Check if the checkpointing table exists.
    1959  *
    1960  ****/
    1961
    1962 int exists_checkpointing_table()
         /* [<][>][^][v][top][bottom][index][help] */
    1963 {
    1964
    1965   char query[MAXCMDLEN];
    1966   SQ_result_set_t *result;
    1967   SQ_row_t *row;
    1968
    1969   sprintf(query, "SHOW tables LIKE 'hs_auxiliary'");
    1970
    1971   execute_sql_query(query, &result);
    1972   if ( (row = SQ_row_next(result)) != NULL )
    1973     {
    1974       SQ_free_result(result);
    1975       return(1);
    1976     }
    1977   else
    1978     {
    1979       SQ_free_result(result);
    1980       return(0);
    1981     }
    1982
    1983
    1984 } /*  exists_checkpointing_table() */
    1985
    1986
    1987
    1988 /****** Checkpointing - crash-recovery test functions ******/
    1989
    1990
    1991 /****
    1992  *
    1993  * get_smallest_serial()
    1994  *
    1995  ****/
    1996
    1997 long get_smallest_serial()
         /* [<][>][^][v][top][bottom][index][help] */
    1998 {
    1999
    2000   long smallest_serial = 0;
    2001
    2002   char query[MAXCMDLEN];
    2003   SQ_result_set_t *result;
    2004   SQ_row_t *row;
    2005
    2006   sprintf (query, "SELECT MIN(serial_id) FROM serials");
    2007
    2008   execute_sql_query(query, &result);
    2009   if ( (row = SQ_row_next(result)) != NULL )
    2010     {
    2011       smallest_serial = row[0] ? atol((const char *)row[0]) : 0;
    2012       /* printf ("Smallest serial ID: %ld\n", smallest_serial); */
    2013     }
    2014
    2015   SQ_free_result(result);
    2016
    2017   return(smallest_serial);
    2018
    2019
    2020 } /* get_smallest_serial() */
    2021
    2022
    2023
    2024 /****
    2025  *
    2026  * get_random_number_in_range(int num1, int num2, int seed)
    2027  * This function gets a random number in a specific range of numbers
    2028  *
    2029  ****/
    2030
    2031 int get_random_number_in_range(int num1, int num2, int seed)
         /* [<][>][^][v][top][bottom][index][help] */
    2032 {
    2033
    2034   int randnum;
    2035   int lonum, hinum, diff;
    2036   double gauge;
    2037
    2038   if (num1 < num2)
    2039     {
    2040       lonum = num1; hinum = num2;
    2041     }
    2042   else
    2043     {
    2044       lonum = num2; hinum = num1;
    2045     }
    2046
    2047   if (lonum == hinum)
    2048     return(lonum);
    2049
    2050   diff = hinum - lonum;
    2051
    2052   if (debug) printf ("Diff: %d\n",diff);
    2053
    2054   /* You need that - otherwise the same number is always cast */
    2055   srand(seed);
    2056
    2057   gauge = (double)rand() / RAND_MAX;
    2058
    2059   if (debug) printf ("Gauge: %f\n",gauge);
    2060
    2061   randnum = lonum + (int)floor((double)diff * gauge);
    2062
    2063   if (debug) printf ("Randnum: %d\n",randnum);
    2064
    2065   return(randnum);
    2066
    2067
    2068 } /* get_random_number_in_range() */
    2069
    2070
    2071
    2072 /****
    2073  *
    2074  * do_crash()
    2075  * Crash the program in a random part of the code
    2076  *
    2077  ****/
    2078
    2079 void do_crash(long crashserial, int is_last_step)
         /* [<][>][^][v][top][bottom][index][help] */
    2080 {
    2081
    2082   char query[MAXCMDLEN];
    2083   SQ_result_set_t *result;
    2084   SQ_row_t *row;
    2085   long serial, objid, seqid;
    2086   int atlast, chkp;
    2087
    2088   /* crash_position has been defined randomly at the beginning of the crashtest */
    2089   if ((code_location++ >= crash_position) || (is_last_step == IS_LAST_STEP))
    2090     /* Crash - no mercy! */
    2091     {
    2092       if (debug) fprintf (stderr, "***Crashing***\nSerial: %ld, Code location: %d; crash position: %d; case branch: %d\n",crashserial, code_location, crash_position, case_branch);
    2093       /* debug stuff, to check if what was written in the checkpointing table is OK */
    2094       sprintf(query, "SELECT serial, object_id, sequence_id, atlast, checkpoint FROM hs_auxiliary");
    2095
    2096       execute_sql_query(query, &result);
    2097       if ( (row = SQ_row_next(result)) != NULL )
    2098         {
    2099           serial = atol((const char *)row[0]);
    2100           objid = atol((const char *)row[1]);
    2101           seqid = atol((const char *)row[2]);
    2102           atlast = atoi((const char *)row[3]);
    2103           chkp = atoi((const char *)row[4]);
    2104           if (debug) fprintf (stderr,"In auxiliary table: serial %ld, object_id %ld, sequence_id %ld, table %d, checkpoint %d\n",
    2105                               serial, objid, seqid, atlast, chkp);
    2106         }
    2107       else
    2108         {
    2109           /* The table is empty! Weird, but return */
    2110           fprintf (stderr, "The checkpointing table exists but is empty!\n");
    2111         }
    2112
    2113       SQ_free_result(result);
    2114
    2115       fflush(stdout);
    2116       sleep(3);
    2117       fflush(stderr);
    2118       sleep(3);
    2119       die;
    2120     }
    2121
    2122 } /* do_crash() */

/* [<][>][^][v][top][bottom][index][help] */