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