## 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] */