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