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

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