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)
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)
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)
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)
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)
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)
257 | {
258 | return(SQ_get_column_string_nocopy(result, row, col));
259 | }
260 | /************************************************************/
261 | int tr_get_dummies(Transaction_t *tr)
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)
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)
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)
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)
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 |