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 |