1    | /***************************************
2    | 
3    |   Functions for handling serials  
4    | 
5    |   Status: NOT REVUED, NOT TESTED
6    | 
7    |  Author(s):       Andrei Robachevsky
8    | 
9    |   ******************/ /******************
10   |   Modification History:
11   |         andrei (08/02/2000) Created.
12   |   ******************/ /******************
13   |   Copyright (c) 2000                              RIPE NCC
14   |  
15   |   All Rights Reserved
16   |   
17   |   Permission to use, copy, modify, and distribute this software and its
18   |   documentation for any purpose and without fee is hereby granted,
19   |   provided that the above copyright notice appear in all copies and that
20   |   both that copyright notice and this permission notice appear in
21   |   supporting documentation, and that the name of the author not be
22   |   used in advertising or publicity pertaining to distribution of the
23   |   software without specific, written prior permission.
24   |   
25   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
26   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
27   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
28   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
29   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
30   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
31   |  ***************************************/
32   | #include "ud.h"
33   | #include "ud_int.h"
34   | #include "ud_tr.h"
35   | 
36   | /************************************************************
37   | * int UD_lock/unlock_serial()                               *
38   | *                                                           *
39   | * Performs lockind/unlocking of the relevant tables         *
40   | *                                                           *
41   | * Returns:                                                  *
42   | * 0 - success                                               *
43   | * Non-zero if error occured (XXX dies now)                  *
44   | *                                                           *
45   | ************************************************************/
46   | int UD_lock_serial(Transaction_t *tr)
47   | {
48   | int sql_err;
49   | 
50   | /* lock all tables we are going to update and commit */	
51   | /* this also includes transaction_rec table, as we update the status */
52   |    sql_err=SQ_execute_query(tr->sql_connection, "LOCK TABLES serials WRITE, failed_transaction WRITE, transaction_rec WRITE ", NULL);
53   |    if (sql_err) { 
54   | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), "LOCK TABLES serials WRITE, failed_transaction WRITE, transaction_rec WRITE ");
55   |         die;
56   |     }
57   |  return(sql_err);
58   | }
59   | 
60   | int UD_unlock_serial(Transaction_t *tr)
61   | {
62   | int sql_err;
63   | 	
64   |    sql_err=SQ_execute_query(tr->sql_connection, "UNLOCK TABLES ", NULL);
65   |    if (sql_err) { 
66   | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), "UNLOCK TABLES");
67   |         die;
68   |     }
69   |  return(sql_err);
70   | }
71   | 
72   | 
73   | /************************************************************
74   | * UD_create_serial()                                        *     
75   | *                                                           *
76   | * Creates a serial record for given transaction             *
77   | * For updates creates 2 serial records (DEL+ADD)            *
78   | *                                                           *
79   | * Important fields of transaction are:                      *
80   | * tr->action        TR_CREATE/TR_UPDATE/TR_DELETE           *
81   | * tr->object_id     should be filled in                     *
82   | * tr->sequence_id   should be set to object updated         *
83   | *                                                           *
84   | * So given object with id=k and seq=n                       *
85   | * Create:  ADD(k,n)                                         *
86   | * Update:  ~S(k,n), ADD(k,n+1)                              *
87   | * Delete:  ~S(k,n), DEL(k,n)                                *
88   | *                                                           *
89   | * Returns:                                                  *
90   | *  currnt serial number.                                    *
91   | *  -1 in case of an error                                   *
92   | *                                                           *
93   | *************************************************************/
94   | 
95   | long UD_create_serial(Transaction_t *tr)
96   | {
97   | GString *query;
98   | long current_serial=0;
99   | int sql_err;
100  | int operation;
101  | long timestamp;
102  | long sequence_id;
103  |    
104  |    if ((query = g_string_sized_new(STR_XL)) == NULL){ 
105  |       tr->succeeded=0;
106  |       tr->error |= ERROR_U_MEM;
107  |       ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
108  |       die;
109  |    }
110  |    
111  |    /* Calculate the object_id - should be max+1 */
112  | //   tr->serial_id = get_minmax_id(tr->sql_connection, "serial_id", "serials", 1) +1;
113  | //   TR_update_id(tr);
114  | 
115  | /* fprintf(stderr, "creating serial\n"); */
116  |   /* if the transaction failed store it in transaction table */
117  |   if(tr->succeeded==0){
118  |     if(ACT_DELETE(tr->action))operation=OP_DEL; else operation=OP_ADD;
119  |     
120  |     g_string_sprintf(query, "INSERT serials SET "
121  |                    "thread_id=%d, object_id=%ld, sequence_id=0, "
122  | 		   "atlast=2, "
123  | 		   "operation=%d ", tr->thread_ins, tr->object_id, operation);
124  |     
125  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
126  | 
127  |     if (sql_err) { 
128  | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
129  | 	die;
130  |         current_serial=-1;
131  |     }
132  |     else {
133  |        current_serial=mysql_insert_id(tr->sql_connection);
134  |        timestamp=time(NULL);
135  | //       if(tr->serial_id!=current_serial) die; /* may be the implementation changed */
136  |        g_string_sprintf(query, "INSERT failed_transaction SET "
137  |                    "thread_id=%d, serial_id=%ld, timestamp=%ld, "
138  | 		   "object='%s' ", tr->thread_ins, current_serial, timestamp, tr->object->object->str);
139  |        /* make a record in transaction table */
140  |        sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
141  |        if (sql_err) { 
142  | 	 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
143  | 	 die;
144  |          current_serial=-1;
145  |        }
146  |     }
147  |     g_string_free(query, TRUE);   
148  |     return(current_serial);
149  |   }  
150  | 
151  | 
152  |   /* if the transaction has succeeded */
153  |   sequence_id=tr->sequence_id;
154  |   /* If this is an update or delete */    
155  |   if(!ACT_CREATE(tr->action)) { 
156  |     /* Increase the sequence_id so we insert correct ADD serial in case of Update */
157  |     sequence_id=tr->sequence_id + 1;
158  |     /* set the atlast field of the latest record for this object to 0 */
159  |     /* because it is moved to history */
160  |     g_string_sprintf(query, "UPDATE serials SET atlast=0, thread_id=%d "
161  |                    "WHERE object_id=%ld "
162  |                    "AND sequence_id=%ld ", tr->thread_upd, tr->object_id, sequence_id-1);
163  |     
164  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
165  |     if (sql_err) { // we can have empty updates, but not errors
166  | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
167  | 	die;
168  |         current_serial=-1;
169  |     }
170  |   }  
171  |   /* XXX below is a code for protocol v2, when updates are atomic */
172  |   /* XXX this is fine (and should always be used) for NRTM, since we */
173  |   /* XXX store failed transactions and playback stream exactly as it comes */
174  |   /* XXX However, for update this may be configurable option */
175  |   /* XXX In case v1 protocol both sections (DEL + ADD) should be executed */
176  |   /* if this a DEL */ 
177  |   if(ACT_DELETE(tr->action)) {   
178  |     /* generate DEL serial */
179  |     g_string_sprintf(query, "INSERT serials SET "
180  |                    "thread_id=%d, object_id=%ld, "
181  |                    "sequence_id=%ld, "
182  |                    "atlast=0, "
183  |                    "operation=%d ", tr->thread_ins, tr->object_id, sequence_id-1, OP_DEL);
184  |     
185  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
186  |     if (sql_err) {
187  | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
188  | 	die;
189  |         current_serial=-1;
190  |     }    
191  |     
192  |     if(current_serial!=-1)current_serial=mysql_insert_id(tr->sql_connection);
193  |     
194  |   }
195  |   else { /* otherwise this is an ADD */
196  | 
197  |    /* now insert creation serial */
198  |    g_string_sprintf(query, "INSERT serials SET "
199  |                   "thread_id=%d, object_id=%ld, "
200  |                   "sequence_id=%ld, "
201  |                   "atlast=1, "
202  |                   "operation=%d ", tr->thread_ins, tr->object_id, sequence_id, OP_ADD);
203  |     
204  |    sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
205  |    if (sql_err) {
206  | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
207  | 	die;
208  |         current_serial=-1;
209  |    }    
210  |     
211  |    if(current_serial!=-1){ 
212  | 	   current_serial=mysql_insert_id(tr->sql_connection);
213  | //	   if(tr->serial_id!=current_serial) die; /* may be the implementation changed */
214  |    }
215  | 
216  |   }
217  |   g_string_free(query, TRUE);        
218  | return(current_serial);
219  | }
220  | /************************************************************
221  | * UD_comrol_serial()                                        *     
222  | *                                                           *
223  | * Commits/Rollbacks a serial record for given transaction   *
224  | * Returns:                                                  *
225  | * 0 in success                                              *
226  | *  -1 in case of an error                                   *
227  | *                                                           *
228  | *************************************************************/
229  | 
230  | char *Q_rollback_serial1="DELETE FROM serials WHERE thread_id=%ld ";
231  | char *Q_rollback_serial2="UPDATE serials SET atlast=1, thread_id=0 WHERE thread_id=%ld ";
232  | char *Q_rollback_transaction="DELETE FROM failed_transaction WHERE thread_id=%ld ";
233  | char *Q_commit_serial="UPDATE serials SET thread_id=0 WHERE thread_id=%ld OR thread_id=%ld ";
234  | char *Q_commit_transaction="UPDATE failed_transaction SET thread_id=0 WHERE thread_id=%ld ";
235  | 
236  | 
237  | 
238  | int UD_comrol_serial(Transaction_t *tr, int commit)
239  | {
240  | GString *query;
241  | int sql_err;
242  | char *Q_transaction;
243  | 
244  |    /* check if something is left in serials from the crash */
245  |    
246  |    if ((query = g_string_sized_new(STR_XL)) == NULL){ 
247  |       ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n"); 
248  |       tr->succeeded=0;
249  |       tr->error |= ERROR_U_MEM;
250  |       return(ERROR_U_MEM); 
251  |    }
252  |    
253  |    /* compose the appropriate query depending on operation (commit/rollback) */
254  |    if(commit) {
255  | 	   /* commit changes to serials table */
256  | 	   g_string_sprintf(query, Q_commit_serial, tr->thread_ins, tr->thread_upd);
257  | 	   sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
258  |            if (sql_err) {
259  | 	     ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
260  |              die;
261  |            }
262  | 	   Q_transaction=Q_commit_transaction;
263  |    } else {
264  | 	   /* delete new insertions */
265  | 	   g_string_sprintf(query, Q_rollback_serial1, tr->thread_ins);
266  | 	   sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
267  |            if (sql_err) {
268  | 	     ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
269  |              die;
270  |            }
271  | 	   /* restore modified atlast */ 
272  |            g_string_sprintf(query, Q_rollback_serial2, tr->thread_upd);
273  | 	   sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
274  |            if (sql_err) {
275  | 	     ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
276  |              die;
277  |            } 
278  | 	   Q_transaction=Q_rollback_transaction;
279  |    }
280  |    
281  |     /* clean up transaction  table */
282  |     g_string_sprintf(query, Q_transaction, tr->thread_ins);
283  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
284  |     if (sql_err) {
285  | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
286  |         die;
287  |     }
288  |  g_string_free(query, TRUE);        
289  |  return(0);
290  | } 
291  |     
292  |