utils/hs_cleanup/hs_cleanup.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- update_prev_serial_of_next_object
- update_serial_in_last
- update_serial_in_history
- archive_history
- archive_last
- delete_history_entry
- delete_last_entry
- main
- usage
- get_highest_serial_before_date
- archive_serials_and_history
- fetch_timestamp_from_last
- get_highest_object_id
- check_if_next_is_deletion
- create_archive_tables
- create_table
- update_history_archive_sequence_id_and_timestamp
- update_history_archive_object_id_and_sequence_id
- update_prev_serial_of_object
- update_serial_of_object
- archive_serial
- archive_failed_transaction
- copy_into_history_archive
- copy_deleted_object_into_history_archive
- delete_serial_entry
- delete_failed_transaction_entry
- delete_entry_from_object_table
- delete_archived_objects
- delete_serial_archive_entry
- delete_history_archive_entry
- find_unreferenced_history_entries
- delete_unreferenced_history_entries
- PushTblObjList
- delete_dummy_history_objects
- lock_last_history_serial_tables
- unlock_all_tables
- optimize_sql_table
- execute_sql_query
- execute_sql_command
- create_auxiliary_table
- reset_auxiliary_table
- drop_auxiliary_table
- update_hs_auxiliary_checkpoint
- crash_recovery
- exists_checkpointing_table
- get_smallest_serial
- get_random_number_in_range
- 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() */