modules/ud/ud_recover.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- TR_create_record
- TR_update_record
- tr_get_sql_record
- tr_get_long
- tr_get_int
- tr_get_str
- tr_get_dummies
- TR_get_record
- TR_delete_record
- TR_recover
- TR_check
1 /***************************************
2 $Revision: 1.8 $
3
4 Functions to keep records for crash recovery
5
6 Status: NOT REVUED, NOT TESTED
7
8 Author(s): Andrei Robachevsky
9
10 ******************/ /******************
11 Modification History:
12 andrei (11/08/2000) Created.
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 #include "rip.h"
35
36 /*************************************************************
37
38 SQL Tables used to keep records needed for crash recovery
39
40 CREATE TABLE transaction_rec (
41 0 transaction_id int(11) DEFAULT '0' NOT NULL auto_increment,
42 1 object_id int(10) unsigned DEFAULT '0' NOT NULL,
43 2 sequence_id int(10) unsigned DEFAULT '1' NOT NULL,
44 3 object_type tinyint(3) unsigned DEFAULT '0' NOT NULL,
45 4 save varchar(256) DEFAULT '' NOT NULL,
46 5 error_script blob DEFAULT '' NOT NULL,
47 6 mode tinyint(4) unsigned DEFAULT '0' NOT NULL,
48 7 succeeded tinyint(4) unsigned DEFAULT '0' NOT NULL,
49 8 action tinyint(4) unsigned DEFAULT '0' NOT NULL,
50 9 status tinyint(10) unsigned DEFAULT '0' NOT NULL,
51 10 clean tinyint(3) DEFAULT '0' NOT NULL,
52 PRIMARY KEY (transaction_id)
53 );
54
55
56
57 CREATE TABLE dummy_rec (
58 transaction_id int(11) DEFAULT '0' NOT NULL,
59 object_id int(10) unsigned DEFAULT '0' NOT NULL,
60 PRIMARY KEY (transaction_id, object_id)
61 );
62
63 *************************************************************/
64
65 /************************************************************
66 * int TR_create_record() *
67 * *
68 * Create TR record *
69 * *
70 * First tries to delete record with the same transaction_id *
71 * ( transaction_id == tr->transaction_id ) *
72 * Then creates a new record in transaction_rec table *
73 * *
74 * Returns: transaction_id *
75 * *
76 ************************************************************/
77
78 long TR_create_record(Transaction_t *tr)
/* [<][>][^][v][top][bottom][index][help] */
79 {
80 SQ_result_set_t *sql_result;
81 GString *query;
82 int sql_err;
83
84 if(IS_STANDALONE(tr->mode)) return(0); /* for loader just return */
85
86 if ((query = g_string_sized_new(STR_L)) == NULL){
87 ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
88 die;
89 }
90 /* delete record if exists*/
91
92 TR_delete_record(tr);
93
94
95 /* compose record */
96
97 tr->action = TR_ACTION(tr->action) + TCP_ROLLBACK;
98
99 g_string_sprintf(query, "INSERT transaction_rec "
100 "SET transaction_id=%ld, "
101 "object_id=%ld, "
102 "sequence_id=%ld, "
103 "object_type=%d, "
104 "mode=%d, "
105 "action=%d, "
106 "status=%d ",
107 tr->transaction_id, tr->object_id, tr->sequence_id, tr->class_type, tr->mode, TR_ACTION(tr->action), TR_STATUS(TCP_ROLLBACK));
108 sql_err=SQ_execute_query(tr->sql_connection, query->str, &sql_result);
109
110
111 /* in case of an error copy error code and return */
112 if(sql_err) {
113 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
114 die;
115 }
116 g_string_free(query, TRUE);
117 return(tr->transaction_id);
118 }
119
120
121 /************************************************************
122 * int TR_update_record() *
123 * *
124 * UPdates TR record (transaction_rec or dummy_rec tables) *
125 * *
126 * Updates the following fields: *
127 * TF_DUMMY - dummy_rec, adding ID's as dummies are created *
128 * TF_SAVE - writes down tr->save *
129 * TF_STATUS - updates status (checkpointing) *
130 * TF_ESCRIPT - saves error script tr->error_script *
131 * *
132 * Returns: transaction_id *
133 * *
134 ************************************************************/
135
136 long TR_update_record(Transaction_t *tr, int field)
/* [<][>][^][v][top][bottom][index][help] */
137 {
138 SQ_result_set_t *sql_result;
139 GString *query;
140 int sql_err;
141
142 if(IS_STANDALONE(tr->mode)) return(0); /* for loader just return */
143
144 if ((query = g_string_sized_new(STR_L)) == NULL){
145 ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
146 die;
147 }
148
149 switch(field){
150 case TF_DUMMY:
151 g_string_sprintf(query, "INSERT dummy_rec "
152 "SET transaction_id=%ld, "
153 "object_id=%ld ",
154 tr->transaction_id, tr->dummy_id[tr->ndummy-1]);
155 break;
156
157 case TF_STATUS:
158 g_string_sprintf(query, "UPDATE transaction_rec "
159 "SET status=%d "
160 "WHERE transaction_id=%ld ",
161 TR_STATUS(tr->action), tr->transaction_id);
162 break;
163
164 case TF_SAVE:
165 g_string_sprintf(query, "UPDATE transaction_rec "
166 "SET save='%s' "
167 "WHERE transaction_id=%ld ",
168 tr->save, tr->transaction_id);
169 break;
170
171 case TF_ESCRIPT:
172 g_string_sprintf(query, "UPDATE transaction_rec "
173 "SET error_script='%s' "
174 "WHERE transaction_id=%ld ",
175 (tr->error_script)->str, tr->transaction_id);
176 break;
177
178 case TF_ID:
179 g_string_sprintf(query, "UPDATE transaction_rec "
180 "SET object_id=%ld, sequence_id=%ld, serial_id=%ld, succeeded=%d "
181 "WHERE transaction_id=%ld ",
182 tr->object_id, tr->sequence_id, tr->serial_id, tr->succeeded, tr->transaction_id);
183 break;
184
185 case TF_CLEAN:
186 g_string_sprintf(query, "UPDATE transaction_rec "
187 "SET clean=1 "
188 "WHERE transaction_id=%ld ",
189 tr->transaction_id);
190 break;
191
192 default: die; break;
193 }
194
195 sql_err=SQ_execute_query(tr->sql_connection, query->str, &sql_result);
196
197
198 /* in case of an error copy error code and return */
199 if(sql_err) {
200 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
201 die;
202 }
203 g_string_free(query, TRUE);
204 return(tr->transaction_id);
205 }
206
207 /* Query the database for transaction record */
208 /* if there is no record with the specified ID - this is a new transaction */
209 /************************************************************/
210 SQ_result_set_t *tr_get_sql_record(SQ_connection_t *sql_connection, long transaction_id)
/* [<][>][^][v][top][bottom][index][help] */
211 {
212 SQ_result_set_t *sql_result;
213 GString *query;
214 int sql_err;
215
216 if ((query = g_string_sized_new(STR_L)) == NULL){
217 ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
218 die;
219 }
220
221 /* compose query */
222 if (transaction_id == TR_LAST)
223 g_string_sprintf(query, "SELECT * FROM transaction_rec WHERE clean=%d", TCP_UNCLEAN);
224 else
225 g_string_sprintf(query, "SELECT * FROM transaction_rec WHERE transaction_id=%ld", transaction_id);
226
227 /* execute query */
228 sql_err=SQ_execute_query(sql_connection, query->str, &sql_result);
229
230
231 /* in case of an error copy error code and return */
232 if(sql_err) {
233 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(sql_connection), query->str);
234 die;
235 }
236 g_string_free(query, TRUE);
237 return(sql_result);
238 }
239
240
241 /************************************************************/
242 long tr_get_long(SQ_result_set_t *result, SQ_row_t *row, int col)
/* [<][>][^][v][top][bottom][index][help] */
243 {
244 long val;
245 if( sscanf(SQ_get_column_string_nocopy(result, row, col), "%ld", &val) < 1 ) { die; }
246 return(val);
247 }
248 /************************************************************/
249 int tr_get_int(SQ_result_set_t *result, SQ_row_t *row, int col)
/* [<][>][^][v][top][bottom][index][help] */
250 {
251 int val;
252 if( sscanf(SQ_get_column_string_nocopy(result, row, col), "%d", &val) < 1 ) { die; }
253 return(val);
254 }
255 /************************************************************/
256 char *tr_get_str(SQ_result_set_t *result, SQ_row_t *row, int col)
/* [<][>][^][v][top][bottom][index][help] */
257 {
258 return(SQ_get_column_string_nocopy(result, row, col));
259 }
260 /************************************************************/
261 int tr_get_dummies(Transaction_t *tr)
/* [<][>][^][v][top][bottom][index][help] */
262 {
263 SQ_result_set_t *sql_result;
264 GString *query;
265 int sql_err;
266 SQ_row_t *sql_row;
267
268 if ((query = g_string_sized_new(STR_L)) == NULL){
269 ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
270 die;
271 }
272
273 /* compose query */
274 g_string_sprintf(query, "SELECT * FROM dummy_rec WHERE transaction_id=%ld", tr->transaction_id);
275 sql_err=SQ_execute_query(tr->sql_connection, query->str, &sql_result);
276
277
278 /* in case of an error copy error code and return */
279 if(sql_err) {
280 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
281 die;
282 }
283 g_string_free(query, TRUE);
284
285 tr->ndummy=0;
286 while ( (sql_row = SQ_row_next(sql_result)) != NULL) {
287 if( sscanf(SQ_get_column_string_nocopy(sql_result, sql_row, DUMMY_OBJECT_ID), "%ld", &(tr->dummy_id[tr->ndummy])) < 1 ) { die; }
288 tr->ndummy++;
289 }
290
291 SQ_free_result(sql_result);
292 return(tr->ndummy);
293 }
294
295 /************************************************************
296 * Transaction_t * TR_get_record() *
297 * *
298 * Get the record left from the failed transaction *
299 * and fill the tr structure *
300 * *
301 * The following fields from transaction are essential: *
302 * *
303 * class_type *
304 * action *
305 * object_id *
306 * sequesnce_id *
307 * save *
308 * ndummy *
309 * dummy_id[] *
310 * error_script *
311 * transaction_id *
312 * *
313 * The following fields are filled in by transaction_new() *
314 * thread_upd *
315 * thread_ins *
316 * standalone *
317 *
318 * Return codes: *
319 * *
320 * NULL - everything is clean, no cleanup is needed *
321 * 1 - the database was recovered successfully *
322 * *
323 ************************************************************/
324 Transaction_t *TR_get_record(SQ_connection_t *sql_connection, long transaction_id)
/* [<][>][^][v][top][bottom][index][help] */
325 {
326 Transaction_t *tr;
327 /* get the record from SQL table */
328 SQ_result_set_t *result;
329 SQ_row_t *row;
330 C_Type_t class_type;
331 int res;
332
333
334 result = tr_get_sql_record(sql_connection, transaction_id);
335 if (result == NULL) return (NULL); /* no further actions */
336
337 /* fill in the Transaction structure */
338 if ((row = SQ_row_next(result))== NULL) {
339 tr = NULL;
340 }
341 else {
342 /* Check if there is more than one row */
343 res = 0;
344 while(SQ_row_next(result))res = -1;
345 if(res == -1) die;
346
347
348 class_type = tr_get_class_type(result, row);
349 if ((tr = transaction_new(sql_connection, class_type)) == NULL) die;
350 tr->transaction_id = tr_get_transaction_id(result, row);
351 tr->object_id = tr_get_object_id(result, row);
352
353 /* Fill in all dummies that were created */
354 tr_get_dummies(tr);
355
356 tr->sequence_id = tr_get_sequence_id(result, row);
357 tr->serial_id = tr_get_serial_id(result, row);
358 tr->save = g_strdup(tr_get_save(result, row));
359 g_string_sprintf(tr->error_script, tr_get_escript(result, row));
360
361
362 /* mode of operation */
363 tr->mode = tr_get_mode(result, row);
364 /* indication of success */
365 tr->succeeded = tr_get_success(result, row);
366 /* action is low byte */
367 tr->action = tr_get_action(result, row);
368 /* status is high byte */
369 tr->action |= (tr_get_status(result, row) <<8);
370 tr->action |= (tr_get_clean(result, row) << 8); /* bit0 bears this flag */
371 }
372
373 SQ_free_result(result);
374 return(tr);
375 }
376
377 /************************************************************
378 * int TR_delete_record() *
379 * *
380 * Deletes all associated sql records *
381 * *
382 * *
383 ************************************************************/
384 void TR_delete_record(Transaction_t *tr)
/* [<][>][^][v][top][bottom][index][help] */
385 {
386 GString *query;
387 int sql_err;
388
389 if(IS_STANDALONE(tr->mode)) return; /* for loader just return */
390
391 /* Delete a record from SQL DB */
392 if ((query = g_string_sized_new(STR_L)) == NULL){
393 ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
394 die;
395 }
396
397 /* compose query */
398 g_string_sprintf(query, "DELETE FROM dummy_rec WHERE transaction_id=%ld", tr->transaction_id);
399 sql_err=SQ_execute_query(tr->sql_connection, query->str, NULL);
400 /* in case of an error copy error code and return */
401 if(sql_err) {
402 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
403 die;
404 }
405 g_string_sprintf(query, "DELETE FROM transaction_rec WHERE transaction_id=%ld", tr->transaction_id);
406 sql_err=SQ_execute_query(tr->sql_connection, query->str, NULL);
407 /* in case of an error copy error code and return */
408 if(sql_err) {
409 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
410 die;
411 }
412
413 g_string_free(query, TRUE);
414
415 }
416
417
418 /************************************************************
419 * int TR_recover() *
420 * *
421 * Cleans up the database after RIP daemon failure *
422 * *
423 * Return codes: *
424 * *
425 * 0 - everything is clean, no cleanup is needed *
426 * 1 - the database was recovered successfully *
427 * *
428 ************************************************************/
429 int TR_recover(SQ_connection_t *sql_connection)
/* [<][>][^][v][top][bottom][index][help] */
430 {
431 int res;
432 Transaction_t * tr;
433 char *act_m;
434
435 /* XXX SQ_db_name() ? */
436 fprintf(stderr, "Checking the Database [%s]...", sql_connection->db);
437
438 /* Get the transaction record */
439 /* XXX for NRTM we may specify transaction_id = 0 ? */
440 if ((tr = TR_get_record(sql_connection, TR_LAST)) == NULL) {
441 /* everything is clean */
442 res = 0;
443 fprintf(stderr, "[OK]\n");
444 ER_inf_va(FAC_SV, 0xFFFFFF, "STATUS:[%s]=OK", sql_connection->db);
445 }
446 else {/* Not everything was perfect :( */
447 if(ACT_CREATE(tr->action))act_m="CREATE";
448 else if(ACT_UPDATE(tr->action))act_m="UPDATE";
449 else act_m="DELETE";
450 ER_inf_va(FAC_SV, 0xFFFFFF, "STATUS:[%s]=FAILED [object_id=%ld, sequence_id=%ld, serial_id=%ld, transaction_id=%ld, action=%s]",
451 sql_connection->db, tr->object_id, tr->sequence_id, tr->serial_id, tr->transaction_id, act_m);
452 fprintf(stderr, "[FAILED]\n"
453 "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"
454 "+ LAST TRANSACTION IS INCOMPLETE. ENTERING CRASH RECOVERY MODE +\n"
455 "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
456 /* Failure occured before the ack was sent */
457 /* Roll back the transaction */
458 /* Delete transaction record (TR) as if it never happened */
459 /************************* R O L L B A C K ***************************/
460 if(TS_ROLLBACK(tr->action)) {
461 fprintf(stderr, " STATUS: Rollback\n");
462 ER_inf_va(FAC_SV, 0xFFFFFF,"STATUS:[%s]=ROLLBACK", sql_connection->db);
463
464 /* don't rollback the transaction if we were to delete the object, but could not */
465 if(!TS_ROLLBACKED(tr->action)){
466 fprintf(stderr, " STATUS: Rollback incomplete, completing...");
467 UD_rollback(tr);
468 CP_ROLLBACK_PASSED(tr->action); TR_update_status(tr);
469 fprintf(stderr, "[OK]\n");
470 ER_inf_va(FAC_SV, 0xFFFFFF,"STATUS:[%s] Rollback incomplete, completing - OK", sql_connection->db);
471 } else { fprintf(stderr, " STATUS: Rollback complete [PASSED]\n");
472 ER_inf_va(FAC_SV, 0xFFFFFF,"STATUS:[%s] Rollback complete - PASSED", sql_connection->db);
473 }
474
475
476 if(!TS_ROLLBACKED_NH(tr->action)){
477 fprintf(stderr, " STATUS: NH rollback incomplete, completing...");
478 NH_rollback(tr->sql_connection);
479 CP_ROLLBACK_NH_PASSED(tr->action); TR_update_status(tr);
480 fprintf(stderr, "[OK]\n");
481 ER_inf_va(FAC_SV, 0xFFFFFF,"STATUS:[%s] NH rollback incomplete, completing - OK", sql_connection->db);
482 } else { fprintf(stderr, " STATUS: NH rollback complete [PASSED]\n");
483 ER_inf_va(FAC_SV, 0xFFFFFF,"STATUS:[%s] NH rollback complete - PASSED", sql_connection->db);
484 }
485 /* In update mode delete TR record. Next time (if any) DBupdate tries to submit, we'll start from scratch */
486 /* In NRTM mode we create a serial record even in case of failure (tr->succeeded ==0)*/
487 /* So in NRTM we need to clean up serials/transaction as well */
488 if(IS_UPDATE(tr->mode)){
489 fprintf(stderr, " STATUS: Serial does not need to be restored, deleting TR...");
490 TR_delete_record(tr);
491 fprintf(stderr, "[OK]\n");
492 ER_inf_va(FAC_SV, 0xFFFFFF,"STATUS:[%s] Serial does not need to be restored, deleting TR - OK", sql_connection->db);
493 } else {
494 fprintf(stderr, " STATUS: Cleaning serial, deleting TR...");
495 if(!TS_CREATED_S(tr->action))
496 UD_rollback_serial(tr);
497 else
498 UD_commit_serial(tr);
499 TR_delete_record(tr);
500 fprintf(stderr, "[OK]\n");
501 ER_inf_va(FAC_SV, 0xFFFFFF,"STATUS:[%s] Cleaning serial, deleting TR - OK", sql_connection->db);
502 }
503
504 res = 1;
505 }
506 /************************* C O M M I T ******************************/
507 else { /* commit */
508 /* The ack was sent */
509 /* Complete the commit */
510 fprintf(stderr, " STATUS: Commit\n");
511 ER_inf_va(FAC_SV, 0xFFFFFF,"STATUS:[%s]=COMMIT", sql_connection->db);
512 /* We keep the transaction record in case DBupdate failed */
513 /* and requests the same transaction after recovery ? */
514 /* Such approach will allow us to avoid 3-way handshaking with DBupdate */
515 /* So we never blocked or timed out during that phase */
516
517 /* XXX But first I implemented another approach (to keep DB tiny/tidy): */
518 /* 1. Process the transaction */
519 /* 2. In case of failure - rollback - NACK */
520 /* 3. Before commit - ACK (UD_ack()) */
521 /* 4. If UD_ack returns an error preserve a tr_record */
522 /* 5. Commit */
523 /* 6. If still alive and UD_ack passed - delete the record - all is clean */
524 /* Otherwise preserve a tr_record */
525
526 if(ACT_DELETE(tr->action)) {
527 /* check if we passed deletion process */
528 if(!TS_DELETED(tr->action)){
529 fprintf(stderr, " STATUS: Delete incomplete, completing...");
530 UD_delete(tr);
531 CP_DELETE_PASSED(tr->action); TR_update_status(tr);
532 fprintf(stderr, "[OK]\n");
533 ER_inf_va(FAC_SV, 0xFFFFFF,"STATUS:[%s] Delete incomplete, completing - OK", sql_connection->db);
534 } else { fprintf(stderr, " STATUS: Delete complete [PASSED]\n");
535 ER_inf_va(FAC_SV, 0xFFFFFF,"STATUS:[%s] Delete complete - PASSED", sql_connection->db);
536 }
537 }
538 else { /* update or create */
539 /* Check if we passed the deletion pass of commit */
540 if(!TS_COMMITTED_I(tr->action)){
541 fprintf(stderr, " STATUS: Commit phase I incomplete, completing...");
542 UD_commit_I(tr);
543 CP_COMMIT_I_PASSED(tr->action); TR_update_status(tr);
544 fprintf(stderr, "[OK]\n");
545 ER_inf_va(FAC_SV, 0xFFFFFF,"STATUS:[%s] Commit phase I incomplete, completing - OK", sql_connection->db);
546 } else { fprintf(stderr, " STATUS: Commit phase I complete [PASSED]\n");
547 ER_inf_va(FAC_SV, 0xFFFFFF,"STATUS:[%s] Commit phase I complete - PASSED", sql_connection->db);
548 }
549 /* Check if we passed the second pass of commit */
550 if(!TS_COMMITTED_II(tr->action)){
551 fprintf(stderr, " STATUS: Commit phase II incomplete, completing...");
552 UD_commit_II(tr);
553 CP_COMMIT_II_PASSED(tr->action); TR_update_status(tr);
554 fprintf(stderr, "[OK]\n");
555 ER_inf_va(FAC_SV, 0xFFFFFF,"STATUS:[%s] Commit phase II incomplete, completing - OK", sql_connection->db);
556 } else { fprintf(stderr, " STATUS: Commit phase II complete [PASSED]\n");
557 ER_inf_va(FAC_SV, 0xFFFFFF,"STATUS:[%s] Commit phase II complete - PASSED", sql_connection->db);
558 }
559 } /* end of delete, create, update specific operations */
560
561 /* Check if we passed the NH repository commit */
562 if(!TS_COMMITTED_NH(tr->action)){
563 fprintf(stderr, " STATUS: NH commit incomplete, completing...");
564 NH_commit(tr->sql_connection);
565 CP_COMMIT_NH_PASSED(tr->action); TR_update_status(tr);
566 fprintf(stderr, "[OK]\n");
567 ER_inf_va(FAC_SV, 0xFFFFFF,"STATUS:[%s] NH commit incomplete, completing - OK", sql_connection->db);
568 } else { fprintf(stderr, " STATUS: NH commit complete [PASSED]\n");
569 ER_inf_va(FAC_SV, 0xFFFFFF,"STATUS:[%s] NH commit complete - PASSED", sql_connection->db);
570 }
571
572 /* create serial file */
573 if(!TS_CREATED_S(tr->action))
574 {
575 fprintf(stderr, " STATUS: Serial rollback and restore...");
576 UD_rollback_serial(tr);
577 if(ACT_UPD_CLLPS(tr->action)) { /* this is a collapsed update (DEL + ADD) */
578 tr->action=TA_DELETE; UD_create_serial(tr);
579 tr->sequence_id++;
580 tr->action=TA_CREATE; UD_create_serial(tr);
581 }else if(ACT_UPD_DUMMY(tr->action)) { /* this was a dummy update - we need only CREATE serial */
582 tr->action=TA_CREATE;
583 tr->sequence_id++; /* because in fact this is an update (sequence_id=2) */
584 UD_create_serial(tr);
585 } else UD_create_serial(tr);
586 CP_CREATE_S_PASSED(tr->action); TR_update_status(tr);
587 }
588 fprintf(stderr, "[OK]\n");
589 ER_inf_va(FAC_SV, 0xFFFFFF,"STATUS:[%s] Serial rollback and restore - OK", sql_connection->db);
590 UD_commit_serial(tr);
591
592 fprintf(stderr, " STATUS: Marking TR as clean...");
593 TR_mark_clean(tr);
594
595 fprintf(stderr, "[OK]\n");
596 ER_inf_va(FAC_SV, 0xFFFFFF,"STATUS:[%s] Marking TR as clean - OK", sql_connection->db);
597 res = 2;
598 }
599 }
600 transaction_free(tr);
601 fprintf(stderr, " STATUS: The Database is clean \n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n");
602 ER_inf_va(FAC_SV, 0xFFFFFF,"STATUS:[%s]=CLEAN", sql_connection->db);
603
604 return(res);
605 }
606
607 /************************************************************
608 * int TR_check() *
609 * *
610 * Checks if the requested transaction has already been *
611 * processed. This could happen when DBupdate crashes while *
612 * RIPupdate successfully completes the transaction. *
613 * *
614 * If this is the case, RIPupdate will return an ack to *
615 * DBupdate as if the transaction was processed again *
616 * *
617 * Return codes: *
618 * 0 - everything is clean - this is a new transaction *
619 * 1 - the stored transaction was re-played *
620 * *
621 ************************************************************/
622 int TR_check(SQ_connection_t *sql_connection, long transaction_id, int sockfd)
/* [<][>][^][v][top][bottom][index][help] */
623 {
624 Transaction_t * tr;
625
626
627 /* transaction_id == 0 means that only one record is maintained */
628 /* therefore it is not possible to replay the transaction */
629 /* and transaction_id does not uniquely identify the transaction */
630 /* suitable for NRTM and for backwards compatibility */
631 if(transaction_id <=0) return(0);
632 /* Get the transaction record */
633 /* XXX for NRTM we may specify transaction_id = 0 ? */
634 if ((tr = TR_get_record(sql_connection, transaction_id)) == NULL) return(0); /* everything is clean */
635
636 /* Check if the record is clean (it should be ) */
637 /* that means that either the transaction finished normally */
638 /* or crash recovery procedure cleaned up the database (and record as well ) */
639 if (TS_CLEAN(tr->action)) {
640 /* send an acknowledgement */
641 /* XXX Wait for ack */
642 /* XXX if ack is timed out just return, else delete the tr_record */
643 /* if(UD_ack(tr)==0) TR_delete_record(tr); */
644
645 /* Send an acknowledgement, append note that transaction was rerun */
646 tr->socket=sockfd;
647 g_string_sprintfa(tr->error_script,"I[%ld]: requested transaction was processed before\n", transaction_id);
648 UD_ack(tr);
649 ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "[%ld] requested transaction was processed before\n", transaction_id);
650 transaction_free(tr);
651 }
652 else {
653 ER_perror(FAC_UD, UD_SQL, "TR is not clean\n");
654 die; /* the record should be clean */
655 }
656 return(1);
657 }
658
659