utils/hs_cleanup/hs_cleanup.c

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

DEFINITIONS

This source file includes following functions.
  1. tblobjPtr
  2. tblObjList
  3. main
  4. usage
  5. get_highest_serial_before_date
  6. archive_serials_and_history
  7. fetch_timestamp_from_last
  8. get_highest_object_id
  9. check_if_next_is_deletion
  10. create_archive_tables
  11. create_table
  12. update_history_archive_sequence_id_and_timestamp
  13. update_history_archive_object_id_and_sequence_id
  14. update_prev_serial_of_object
  15. update_serial_of_object
  16. archive_serial
  17. archive_failed_transaction
  18. copy_into_history_archive
  19. copy_deleted_object_into_history_archive
  20. delete_serial_entry
  21. delete_failed_transaction_entry
  22. delete_entry_from_object_table
  23. delete_archived_objects
  24. delete_serial_archive_entry
  25. delete_history_archive_entry
  26. find_unreferenced_history_entries
  27. delete_unreferenced_history_entries
  28. PushTblObjList
  29. delete_dummy_history_objects
  30. lock_last_history_serial_tables
  31. unlock_all_tables
  32. optimize_sql_table
  33. execute_sql_query
  34. execute_sql_command
  35. create_auxiliary_table
  36. reset_auxiliary_table
  37. drop_auxiliary_table
  38. update_hs_auxiliary_checkpoint
  39. crash_recovery
  40. exists_checkpointing_table
  41. get_smallest_serial
  42. get_random_number_in_range
  43. 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;
     /* [<][>][^][v][top][bottom][index][help] */
 141 
 142 typedef struct table_object {
 143   long objid;
 144   long seqid;
 145   tblobjPtr next;
 146 } tblObjList;
     /* [<][>][^][v][top][bottom][index][help] */
 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)
 188 #define update_serial_in_last(obj_id, seq_id, ser_id) update_serial_of_object(obj_id, seq_id, ser_id, "last")
 189 #define update_serial_in_history(obj_id, seq_id, ser_id) update_serial_of_object(obj_id, seq_id, ser_id, "history")
 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")
 197 #define archive_last(ser_id, obj_id, seq_id) copy_into_history_archive(ser_id, obj_id, seq_id, "last")
 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")
 207 #define delete_last_entry(obj_id, seq_id) delete_entry_from_object_table(obj_id, seq_id, "last")
 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] */