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

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