## modules/ud/ud_recover.c

/* [<][>][^][v][top][bottom][index][help] */

## FUNCTIONS

This source file includes following functions.

1. TR_create_record
2. TR_update_record
3. tr_get_sql_record
4. tr_get_long
5. tr_get_int
6. tr_get_str
7. tr_get_dummies
8. TR_get_record
9. TR_delete_record
10. TR_recover
11. TR_check

       1 /***************************************
       2   $Revision: 1.2 $
       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) return (NULL); /* no further actions */
     339
     340
     341  /* Check if there is more than one row */
     342   res = 0;
     343   while(SQ_row_next(result))res = -1;
     344   if(res == -1) die;
     345
     346
     347   class_type = tr_get_class_type(result, row);
     348   if ((tr = transaction_new(sql_connection, class_type)) == NULL) die;
     349   tr->object_id = tr_get_object_id(result, row);
     350
     351 /* Fill in all dummies that were created */
     352   tr_get_dummies(tr);
     353
     354   tr->sequence_id = tr_get_sequence_id(result, row);
     355   tr->serial_id = tr_get_serial_id(result, row);
     356   tr->save = g_strdup(tr_get_save(result, row));
     357   g_string_sprintf(tr->error_script, tr_get_escript(result, row));
     358
     359
     360   /* mode of operation */
     361   tr->mode = tr_get_mode(result, row);
     362   /* indication of success */
     363   tr->succeeded = tr_get_success(result, row);
     364   /* action is low byte */
     365   tr->action = tr_get_action(result, row);
     366   /* status is high byte */
     367   tr->action |= (tr_get_status(result, row) <<8);
     368   tr->action |= (tr_get_clean(result, row) << 8); /* bit0 bears this flag */
     369
     370   SQ_free_result(result);
     371   return(tr);
     372 }
     373
     374 /************************************************************
     375 * int TR_delete_record()                                    *
     376 *                                                           *
     377 * Deletes all associated sql records                        *
     378 *                                                           *
     379 *                                                           *
     380 ************************************************************/
     381 void TR_delete_record(Transaction_t *tr)
         /* [<][>][^][v][top][bottom][index][help] */
     382 {
     383 GString *query;
     384 int sql_err;
     385
     386   if(tr->load_pass != 0) return; /* for fast loader just return */
     387
     388   /* Delete a record from SQL DB */
     389   if ((query = g_string_sized_new(STR_L)) == NULL){
     390    ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
     391    die;
     392   }
     393
     394   /* compose query */
     395   g_string_sprintf(query, "DELETE FROM dummy_rec WHERE transaction_id=%ld", tr->transaction_id);
     396   sql_err=SQ_execute_query(tr->sql_connection, query->str, NULL);
     397   /* in case of an error copy error code and return */
     398   if(sql_err) {
     399    ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
     400    die;
     401   }
     402   g_string_sprintf(query, "DELETE FROM transaction_rec WHERE transaction_id=%ld", tr->transaction_id);
     403   sql_err=SQ_execute_query(tr->sql_connection, query->str, NULL);
     404   /* in case of an error copy error code and return */
     405   if(sql_err) {
     406    ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
     407    die;
     408   }
     409
     410   g_string_free(query, TRUE);
     411
     412 }
     413
     414
     415 /************************************************************
     416 * int TR_recover()                                          *
     417 *                                                           *
     418 * Cleans up the database after RIP daemon failure           *
     419 *                                                           *
     420 * Return codes:                                             *
     421 *                                                           *
     422 * 0 - everything is clean, no cleanup is needed             *
     423 * 1 - the database was recovered successfully               *
     424 *                                                           *
     425 ************************************************************/
     426 int TR_recover(SQ_connection_t *sql_connection)
         /* [<][>][^][v][top][bottom][index][help] */
     427 {
     428 int res;
     429 Transaction_t * tr;
     430
     431 /* XXX SQ_db_name() ? */
     432  fprintf(stderr, "Checking the Database [%s]...", sql_connection->db);
     433
     434  /* Get the transaction record */
     435  /* XXX for NRTM we may specify transaction_id = 0 ? */
     436  if ((tr = TR_get_record(sql_connection, TR_LAST)) == NULL) {
     437     /* everything is clean */
     438     res = 0;
     439     fprintf(stderr, "[OK]\n");
     440  }
     441  else {/* Not everything was perfect :( */
     442     fprintf(stderr, "[FAILED]\n"
     443                     "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"
     444                     "+ LAST TRANSACTION IS INCOMPLETE. ENTERING CRASH RECOVERY MODE +\n"
     445                     "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
     446    /* Failure occured before the ack was sent */
     447    /* Roll back the transaction */
     448    /* Delete transaction record (TR) as if it never happened */
     449    /************************* R O L L B A C K ***************************/
     450    if(TS_ROLLBACK(tr->action)) {
     451      fprintf(stderr, "  STATUS: Rollback\n");
     452
     453     /* don't rollback the transaction if we were to delete the object, but could not */
     454      if(!TS_ROLLBACKED(tr->action)){
     455              fprintf(stderr, "  STATUS: Rollback incomplete, completing...");
     456              UD_rollback(tr);
     457              CP_ROLLBACK_PASSED(tr->action); TR_update_status(tr);
     458              fprintf(stderr, "[OK]\n");
     459      } else  fprintf(stderr, "  STATUS: Rollback complete [PASSED]\n");
     460
     461
     462      if(!TS_ROLLBACKED_NH(tr->action)){
     463              fprintf(stderr, "  STATUS: NH rollback incomplete, completing...");
     464              NH_rollback(tr->sql_connection);
     465              CP_ROLLBACK_NH_PASSED(tr->action); TR_update_status(tr);
     466              fprintf(stderr, "[OK]\n");
     467      } else  fprintf(stderr, "  STATUS: NH rollback complete [PASSED]\n");
     468      /* In update mode delete TR record. Next time (if any) DBupdate tries to submit, we'll start from scratch */
     469      /* In NRTM mode we create a serial record even in case of failure (tr->succeeded ==0)*/
     470      /* So in NRTM we need to clean up serials/transaction as well */
     471      if(IS_UPDATE(tr->mode)){
     472              fprintf(stderr, "  STATUS: Serial does not need to be restored, deleting TR...");
     473              TR_delete_record(tr);
     474              fprintf(stderr, "[OK]\n");
     475      } else {
     476              fprintf(stderr, "  STATUS: Cleaning serial, deleting TR...");
     477              if(!TS_CREATED_S(tr->action))
     478                UD_rollback_serial(tr);
     479              else
     480                UD_commit_serial(tr);
     481              TR_delete_record(tr);
     482              fprintf(stderr, "[OK]\n");
     483      }
     484
     485      res = 1;
     486    }
     487    /************************* C O M M I T ******************************/
     488    else { /* commit */
     489     /* The ack was sent */
     490     /* Complete the commit */
     491     fprintf(stderr, "  STATUS: Commit\n");
     492     /* We keep the transaction record in case DBupdate failed */
     493     /* and requests the same transaction after recovery ? */
     494     /* Such approach will allow us to avoid 3-way handshaking with DBupdate */
     495     /* So we never blocked or timed out during that phase */
     496
     497     /* XXX But first I implemented another approach (to keep DB tiny/tidy): */
     498     /* 1. Process the transaction */
     499     /* 2. In case of failure - rollback - NACK */
     500     /* 3. Before commit - ACK (UD_ack()) */
     501     /* 4. If UD_ack returns an error preserve a tr_record */
     502     /* 5. Commit */
     503     /* 6. If still alive and UD_ack passed - delete the record - all is clean */
     504     /*    Otherwise preserve a tr_record */
     505
     506     if(ACT_DELETE(tr->action)) {
     507      /* check if we passed deletion process */
     508      if(!TS_DELETED(tr->action)){
     509              fprintf(stderr, "  STATUS: Delete incomplete, completing...");
     510              UD_delete(tr);
     511              CP_DELETE_PASSED(tr->action); TR_update_status(tr);
     512              fprintf(stderr, "[OK]\n");
     513      } else  fprintf(stderr, "  STATUS: Delete complete [PASSED]\n");
     514     }
     515     else { /* update or create */
     516      /* Check if we passed the deletion pass of commit */
     517      if(!TS_COMMITTED_I(tr->action)){
     518              fprintf(stderr, "  STATUS: Commit phase I incomplete, completing...");
     519              UD_commit_I(tr);
     520              CP_COMMIT_I_PASSED(tr->action); TR_update_status(tr);
     521              fprintf(stderr, "[OK]\n");
     522      } else  fprintf(stderr, "  STATUS: Commit phase I complete [PASSED]\n");
     523      /* Check if we passed the second pass of commit */
     524      if(!TS_COMMITTED_II(tr->action)){
     525              fprintf(stderr, "  STATUS: Commit phase II incomplete, completing...");
     526              UD_commit_II(tr);
     527              CP_COMMIT_II_PASSED(tr->action); TR_update_status(tr);
     528              fprintf(stderr, "[OK]\n");
     529      } else  fprintf(stderr, "  STATUS: Commit phase II complete [PASSED]\n");
     530     } /* end of delete, create, update specific operations */
     531
     532      /* Check if we passed the NH repository commit */
     533      if(!TS_COMMITTED_NH(tr->action)){
     534              fprintf(stderr, "  STATUS: NH commit incomplete, completing...");
     535              NH_commit(tr->sql_connection);
     536              CP_COMMIT_NH_PASSED(tr->action); TR_update_status(tr);
     537              fprintf(stderr, "[OK]\n");
     538      } else  fprintf(stderr, "  STATUS: NH commit complete [PASSED]\n");
     539
     540
     541      /* create serial file */
     542      if(!TS_CREATED_S(tr->action))
     543      {
     544        fprintf(stderr, "  STATUS: Serial rollback and restore...");
     545        UD_rollback_serial(tr);
     546        if(ACT_UPD_CLLPS(tr->action)) { /* this is a collapsed update (DEL + ADD) */
     547           tr->action=TA_DELETE; UD_create_serial(tr);
     548           tr->sequence_id++;
     549           tr->action=TA_CREATE; UD_create_serial(tr);
     550        }else if(ACT_UPD_DUMMY(tr->action)) { /* this was a dummy update - we need only CREATE serial */
     551           tr->action=TA_CREATE;
     552           tr->sequence_id++; /* because in fact this is an update (sequence_id=2) */
     553           UD_create_serial(tr);
     554        } else UD_create_serial(tr);
     555        CP_CREATE_S_PASSED(tr->action); TR_update_status(tr);
     556      }
     557      fprintf(stderr, "[OK]\n");
     558      UD_commit_serial(tr);
     559
     560      fprintf(stderr, "  STATUS: Marking TR as clean...");
     561      TR_mark_clean(tr);
     562
     563      fprintf(stderr, "[OK]\n");
     564
     565      res = 2;
     566   }
     567  }
     568  transaction_free(tr);
     569  fprintf(stderr, "  STATUS: The Database is clean \n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n");
     570
     571  return(res);
     572 }
     573
     574 /************************************************************
     575 * int TR_check()                                            *
     576 *                                                           *
     577 * Checks if the requested transaction has already been      *
     578 * processed. This could happen when DBupdate crashes while  *
     579 * RIPupdate successfully completes the transaction.         *
     580 *                                                           *
     581 * If this is the case, RIPupdate will return an ack to      *
     582 * DBupdate as if the transaction was processed again        *
     583 *                                                           *
     584 * Return codes:                                             *
     585 * 0 - everything is clean - this is a new transaction       *
     586 * 1 - the stored transaction was re-played                  *
     587 *                                                           *
     588 ************************************************************/
     589 int TR_check(SQ_connection_t *sql_connection, long transaction_id, int sockfd)
         /* [<][>][^][v][top][bottom][index][help] */
     590 {
     591 Transaction_t * tr;
     592
     593
     594  /* transaction_id == 0 means that only one record is maintained */
     595  /* therefore it is not possible to replay the transaction */
     596  /* and transaction_id does not uniquely identify the transaction */
     597  /* suitable for NRTM and for backwards compatibility */
     598  if(transaction_id <=0) return(0);
     599  /* Get the transaction record */
     600  /* XXX for NRTM we may specify transaction_id = 0 ? */
     601  if ((tr = TR_get_record(sql_connection, transaction_id)) == NULL) return(0); /* everything is clean */
     602
     603  /* Check if the record is clean (it should be ) */
     604  /* that means that either the transaction finished normally */
     605  /* or crash recovery procedure cleaned up the database (and record as well ) */
     606  if (TS_CLEAN(tr->action)) {
     607    /* send an acknowledgement */
     608    /* XXX Wait for ack */
     609    /* XXX if ack is timed out just return, else delete the tr_record */
     610    /* if(UD_ack(tr)==0) TR_delete_record(tr); */
     611
     612    /* Send an acknowledgement, append note that transaction was rerun */
     613    tr->socket=sockfd;
     614    g_string_sprintfa(tr->error_script,"I[%ld]: requested transaction was processed before\n", transaction_id);
     615    UD_ack(tr);
     616    ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "%s requested transaction was processed before [%d]\n", UD_TAG, transaction_id);
     617    transaction_free(tr);
     618  }
     619  else {
     620          ER_perror(FAC_UD, UD_SQL, "TR is not clean\n");
     621          die; /* the record should be clean */
     622  }
     623  return(1);
     624 }
     625
     626

/* [<][>][^][v][top][bottom][index][help] */