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