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   | void BEKILLED()
37   | {
38   | 
39   |  kill(getpid(), SIGKILL); 
40   | 
41   | 
42   | }
43   |          
44   | /************************************************************
45   | * int UD_lock/unlock_serial()                               *
46   | *                                                           *
47   | * Performs lockind/unlocking of the relevant tables         *
48   | *                                                           *
49   | * Returns:                                                  *
50   | * 0 - success                                               *
51   | * Non-zero if error occured (XXX dies now)                  *
52   | *                                                           *
53   | ************************************************************/
54   | int UD_lock_serial(Transaction_t *tr)
55   | {
56   | int sql_err;
57   | 
58   | /* lock all tables we are going to update and commit */	
59   | /* this also includes transaction_rec table, as we update the status */
60   |    sql_err=SQ_execute_query(tr->sql_connection, "LOCK TABLES serials WRITE, failed_transaction WRITE, transaction_rec WRITE ", NULL);
61   |    if (sql_err) { 
62   | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), "LOCK TABLES serials WRITE, failed_transaction WRITE, transaction_rec WRITE ");
63   |         die;
64   |     }
65   |  return(sql_err);
66   | }
67   | 
68   | int UD_unlock_serial(Transaction_t *tr)
69   | {
70   | int sql_err;
71   | 	
72   |    sql_err=SQ_execute_query(tr->sql_connection, "UNLOCK TABLES ", NULL);
73   |    if (sql_err) { 
74   | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), "UNLOCK TABLES");
75   |         die;
76   |     }
77   |  return(sql_err);
78   | }
79   | 
80   | 
81   | /************************************************************
82   | * UD_create_serial()                                        *     
83   | *                                                           *
84   | * Creates a serial record for given transaction             *
85   | *                                                           *
86   | * Important fields of transaction are:                      *
87   | * tr->action        TR_CREATE/TR_UPDATE/TR_DELETE           *
88   | * tr->object_id     should be filled in                     *
89   | * tr->sequence_id   should be set to object updated         *
90   | *                                                           *
91   | * So given object with id=k and seq=n                       *
92   | * Create:  ADD(k,n)                                         *
93   | * Update:  ~S(k,n), ADD(k,n+1)                              *
94   | * Delete:  ~S(k,n), DEL(k,n)                                *
95   | *                                                           *
96   | * Returns:                                                  *
97   | *  current serial number.                                    *
98   | *  -1 in case of an error                                   *
99   | *                                                           *
100  | *************************************************************/
101  | 
102  | long UD_create_serial(Transaction_t *tr)
103  | {
104  | GString *query;
105  | //long current_serial=0;
106  | int sql_err;
107  | int operation;
108  | long timestamp;
109  | long sequence_id;
110  |    
111  |    if ((query = g_string_sized_new(STR_XL)) == NULL){ 
112  |       tr->succeeded=0;
113  |       tr->error |= ERROR_U_MEM;
114  |       ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
115  |       die;
116  |    }
117  |    
118  |  /* Calculate the object_id - should be max+1 */
119  |  /* XXX we cannot use autoincrement with MyISAM tables */
120  |  /* XXX because they keep the max inserted id even if  */
121  |  /* XXX it was deleted later, thus causing gaps we don't want */
122  |     tr->serial_id = SQ_get_max_id(tr->sql_connection, "serial_id", "serials") +1;
123  | 
124  |   /* if the transaction failed store it in transaction table */
125  |   if(tr->succeeded==0){
126  |     if(ACT_DELETE(tr->action))operation=OP_DEL; else operation=OP_ADD;
127  |     
128  |     g_string_sprintf(query, "INSERT serials SET "
129  |                    "thread_id=%d, serial_id=%ld, object_id=%ld, sequence_id=0, "
130  | 		   "atlast=2, "
131  | 		   "operation=%d ", tr->thread_ins, tr->serial_id, tr->object_id, operation);
132  |     
133  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
134  | 
135  |     if (sql_err) { 
136  | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
137  | //	current_serial=-1;
138  | 	die;
139  |     }
140  |     else {
141  |        timestamp=time(NULL);
142  |        g_string_sprintf(query, "INSERT failed_transaction SET "
143  |                    "thread_id=%d, serial_id=%ld, timestamp=%ld, "
144  | 		   "object='%s' ", tr->thread_ins, tr->serial_id, timestamp, tr->object->object->str);
145  |        /* make a record in transaction table */
146  |        sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
147  |        if (sql_err) { 
148  | 	 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
149  | 	 die;
150  |        }
151  |     }
152  |     g_string_free(query, TRUE);   
153  |     return(tr->serial_id);
154  |   }  
155  | 
156  | 
157  |   /* if the transaction has succeeded */
158  |   sequence_id=tr->sequence_id;
159  |   /* If this is an update or delete */    
160  |   if(!ACT_CREATE(tr->action)) { 
161  |     /* Increase the sequence_id so we insert correct ADD serial in case of Update */
162  |     sequence_id=tr->sequence_id + 1;
163  |     
164  |     /* set the atlast field of the latest record for this object to 0 */
165  |     /* because it is moved to history */
166  |     g_string_sprintf(query, "UPDATE serials SET atlast=0, thread_id=%d "
167  |                    "WHERE object_id=%ld "
168  |                    "AND sequence_id=%ld ", tr->thread_upd, tr->object_id, sequence_id-1);
169  |     
170  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
171  |     if (sql_err) { // we can have empty updates, but not errors
172  | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
173  | 	die;
174  |     }
175  |   }  
176  |   /* XXX below is a code for protocol v2, where updates are atomic */
177  |   /* XXX this is fine (and should always be used) for NRTM, since we */
178  |   /* XXX store failed transactions and playback stream exactly as it comes */
179  |   /* XXX However, for update this may be configurable option */
180  |   /* XXX In case v1 protocol both sections (DEL + ADD) should be executed */
181  |   
182  |   /* get the next serial_id */
183  |   /* XXX we cannot use autoincrement with MyISAM tables */
184  |   /* XXX because they keep the max inserted id even if  */
185  |   /* XXX it was deleted later, thus causing gaps we don't want */
186  |   tr->serial_id = SQ_get_max_id(tr->sql_connection, "serial_id", "serials") +1;
187  |   
188  |   /* if this a DEL */ 
189  |   
190  |   
191  |   if(ACT_DELETE(tr->action)) {   
192  |     /* generate DEL serial */
193  |         
194  |     g_string_sprintf(query, "INSERT serials SET "
195  |                    "thread_id=%d, serial_id=%ld, object_id=%ld, "
196  |                    "sequence_id=%ld, "
197  |                    "atlast=0, "
198  |                    "operation=%d ", tr->thread_ins, tr->serial_id, tr->object_id, sequence_id-1, OP_DEL);
199  |     
200  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
201  |     if (sql_err) {
202  | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
203  | 	die;
204  |     }    
205  |     
206  |   }
207  |   else { /* otherwise this is an ADD */
208  | 
209  |    /* now insert creation serial */
210  |    g_string_sprintf(query, "INSERT serials SET "
211  |                   "thread_id=%d, serial_id=%ld, object_id=%ld, "
212  |                   "sequence_id=%ld, "
213  |                   "atlast=1, "
214  |                   "operation=%d ", tr->thread_ins, tr->serial_id, tr->object_id, sequence_id, OP_ADD);
215  |     
216  |    sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
217  |    if (sql_err) {
218  | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
219  | 	die;
220  |    }    
221  | 
222  |   }
223  |   g_string_free(query, TRUE);        
224  | return(tr->serial_id);
225  | }
226  | /************************************************************
227  | * UD_comrol_serial()                                        *     
228  | *                                                           *
229  | * Commits/Rollbacks a serial record for given transaction   *
230  | * Returns:                                                  *
231  | * 0 in success                                              *
232  | *  -1 in case of an error                                   *
233  | *                                                           *
234  | *************************************************************/
235  | 
236  | char *Q_rollback_serial1="DELETE FROM serials WHERE thread_id=%ld ";
237  | char *Q_rollback_serial2="UPDATE serials SET atlast=1, thread_id=0 WHERE thread_id=%ld ";
238  | char *Q_rollback_transaction="DELETE FROM failed_transaction WHERE thread_id=%ld ";
239  | char *Q_commit_serial="UPDATE serials SET thread_id=0 WHERE thread_id=%ld OR thread_id=%ld ";
240  | char *Q_commit_transaction="UPDATE failed_transaction SET thread_id=0 WHERE thread_id=%ld ";
241  | 
242  | 
243  | 
244  | int UD_comrol_serial(Transaction_t *tr, int commit)
245  | {
246  | GString *query;
247  | int sql_err;
248  | char *Q_transaction;
249  | 
250  |    /* check if something is left in serials from the crash */
251  |    
252  |    if ((query = g_string_sized_new(STR_XL)) == NULL){ 
253  |       ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n"); 
254  |       tr->succeeded=0;
255  |       tr->error |= ERROR_U_MEM;
256  |       return(ERROR_U_MEM); 
257  |    }
258  |    
259  |    /* compose the appropriate query depending on operation (commit/rollback) */
260  |    if(commit) {
261  | 	   /* commit changes to serials table */
262  | 	   g_string_sprintf(query, Q_commit_serial, tr->thread_ins, tr->thread_upd);
263  | 	   sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
264  |            if (sql_err) {
265  | 	     ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
266  |              die;
267  |            }
268  | 	   Q_transaction=Q_commit_transaction;
269  |    } else {
270  | 	   /* delete new insertions */
271  | 	   g_string_sprintf(query, Q_rollback_serial1, tr->thread_ins);
272  | 	   sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
273  |            if (sql_err) {
274  | 	     ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
275  |              die;
276  |            }
277  | 	   /* restore modified atlast */ 
278  |            g_string_sprintf(query, Q_rollback_serial2, tr->thread_upd);
279  | 	   sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
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  | 	   Q_transaction=Q_rollback_transaction;
285  |    }
286  |    
287  |     /* clean up transaction  table */
288  |     g_string_sprintf(query, Q_transaction, tr->thread_ins);
289  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
290  |     if (sql_err) {
291  | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
292  |         die;
293  |     }
294  |  g_string_free(query, TRUE);        
295  |  return(0);
296  | } 
297  |     
298  |