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)
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) 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)
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)
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)
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  |