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