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