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  |