1    | /***************************************
2    |   $Revision: 1.5 $
3    | 
4    |   rollback(), commit(), delete() - rollback, commit update transaction, delete an object
5    | 
6    |   Status: NOT REVUED, NOT TESTED
7    | 
8    |  Author(s):       Andrei Robachevsky
9    | 
10   |   ******************/ /******************
11   |   Modification History:
12   |         andrei (17/01/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   | #include "ud.h"
34   | #include "ud_int.h"
35   | #include "ud_comrol.h"
36   | 
37   | #define RIPE_REG 17
38   | 
39   | /************************************************************
40   | * int rollback(Transaction_t *tr)
41   | * Rollback the transaction
42   | *
43   | * **********************************************************/ 
44   | int rollback(Transaction_t *tr) {
45   | SQ_result_set_t * sql_result;
46   | GString *query;
47   | long sequence_id;
48   | int i, j;
49   | 
50   |  if(tr->action==TR_DELETE) return(0);
51   | 	
52   |  if ((query = g_string_sized_new(STR_XXL)) == NULL){ 
53   |    fprintf(stderr, "E: cannot allocate gstring\n"); 
54   |    tr->succeeded=0;
55   |    tr->error |= ERROR_U_MEM;
56   |    return(ERROR_U_MEM); }
57   | 
58   | /* Lock all relevant tables */
59   |     if(tr->class_type==C_IR) g_string_sprintf(query, "LOCK TABLES inet_rtr WRITE"); 
60   |     else g_string_sprintf(query, "LOCK TABLES %s WRITE,", tables[tr->class_type][0]);
61   |     
62   |     for (i=1; tables[tr->class_type][i] != NULL; i++) 
63   |       g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
64   |     
65   |     for (i=TAB_START; tables[tr->class_type][i] != NULL; i++)
66   |       g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
67   |     
68   |     g_string_sprintfa(query, " last WRITE, history WRITE ");
69   |     
70   |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
71   | 
72   | //fprintf(stderr,"%s\n", query->str);
73   | 
74   | 
75   | /* Process AUX and LEAF tables */
76   |     for (i=TAB_START; tables[tr->class_type][i] != NULL; i++) {
77   |     /* Delete what has been inserted */
78   |     g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld AND thread_id=%d", tables[tr->class_type][i], tr->object_id, tr->thread_ins);
79   |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
80   | //    fprintf(stderr, "D: query (rollback): %s\n", query->str);
81   | 
82   |     /* Normalize what has been updated/touched */
83   |     g_string_sprintf(query, "UPDATE %s SET thread_id=0 WHERE object_id=%ld AND thread_id=%d", tables[tr->class_type][i], tr->object_id, tr->thread_upd);
84   |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
85   | //    fprintf(stderr, "D: query (rollback): %s\n", query->str);
86   |   }
87   | 
88   |   if(tr->class_type==C_IR){
89   | /* Don't forget about inet_rtr, since we haven't included it in the Tables. */
90   |     g_string_sprintf(query, "DELETE FROM inet_rtr WHERE  object_id=%ld AND thread_id=%d", tr->object_id, tr->thread_ins);
91   |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
92   | //    fprintf(stderr, "D: query (rollback): %s\n", query->str);
93   |     
94   |     g_string_sprintf(query, "UPDATE inet_rtr SET thread_id=0 WHERE  object_id=%ld AND thread_id=%d", tr->object_id, tr->thread_upd);
95   |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
96   | //    fprintf(stderr, "D: query (rollback): %s\n", query->str);
97   |   }
98   |   else {
99   | /* Process the MAIN tables (they appear first in the table list */
100  |     g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld AND thread_id=%d", tables[tr->class_type][0], tr->object_id, tr->thread_ins);
101  |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
102  | //    fprintf(stderr, "D: query (rollback): %s\n", query->str);
103  |     
104  |     g_string_sprintf(query, "UPDATE %s SET thread_id=0 WHERE object_id=%ld AND thread_id=%d", tables[tr->class_type][0], tr->object_id, tr->thread_upd);
105  |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
106  | //    fprintf(stderr, "D: query (rollback): %s\n", query->str);  
107  |   }  
108  | 
109  | /* Now tables  that might be affected by dummies */
110  |     for(j=0; j < tr->ndummy; j++) 
111  |     for (i=1; tables[tr->class_type][i] != NULL; i++) {
112  |     	g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld ", tables[tr->class_type][i], tr->dummy_id[j]);
113  |     	sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
114  | //    	fprintf(stderr, "D: query (rollback DUMMY): %s\n", query->str);
115  |     } 
116  | 
117  | /* Rollback last and history tables */
118  |   if(tr->action==TR_UPDATE) { // so we are updating an object
119  |    g_string_sprintf(query, "DELETE FROM history WHERE object_id=%ld AND sequence_id=%ld", tr->object_id, tr->sequence_id);
120  |    sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
121  | //   fprintf(stderr, "D: query (rollback): %s\n", query->str);    
122  | // we do not need to delete a row in the last for updates    
123  |   }
124  |   else { // we failed to create an object
125  |    sequence_id=1; // sequence start == 1
126  |    g_string_sprintf(query, "DELETE FROM last WHERE object_id=%ld AND sequence_id=%ld", tr->object_id, sequence_id);
127  |    sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
128  | //   fprintf(stderr, "D: query (rollback): %s\n", query->str);
129  |   }
130  | 
131  | 
132  |   for(j=0; j < tr->ndummy; j++){// if dummies have been created
133  | 	 g_string_sprintf(query, "DELETE FROM last WHERE object_id=%ld ", tr->dummy_id[j]);
134  | 	 sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
135  | //       fprintf(stderr, "D: query (rollback DUMMY): %s\n", query->str);
136  |   }
137  |   
138  |   /* Unlock all tables */
139  |   g_string_sprintf(query, "UNLOCK TABLES ");
140  |   sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
141  | 
142  | //fprintf(stderr,"%s\n", query->str);
143  |   
144  |   g_string_free(query, TRUE);
145  |   return(0);
146  | } /* rollback() */
147  | 
148  | /************************************************************
149  | * int commit(Transaction_t *tr)
150  | * Commit the transaction
151  | *
152  | * **********************************************************/ 
153  | int commit(Transaction_t *tr) {
154  | rx_tree_t  *mytree;
155  | SQ_result_set_t * sql_result;
156  | GString *query;
157  | int err=0;
158  | int i,j;
159  | 
160  | if(tr->action==TR_DELETE) return(0);
161  | 
162  |  if ((query = g_string_sized_new(STR_XXL)) == NULL){ 
163  |    fprintf(stderr, "E: cannot allocate gstring\n"); 
164  |    tr->succeeded=0;
165  |    tr->error|=ERROR_U_MEM;
166  |    return(ERROR_U_MEM); 
167  |  }
168  | 
169  | /* Lock all relevant tables */
170  |     if(tr->class_type==C_IR) g_string_sprintf(query, "LOCK TABLES inet_rtr WRITE"); 
171  |     else g_string_sprintf(query, "LOCK TABLES %s WRITE,", tables[tr->class_type][0]);
172  |     
173  |     for (i=1; tables[tr->class_type][i] != NULL; i++) 
174  |       g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
175  |     
176  |     for (i=TAB_START; tables[tr->class_type][i] != NULL; i++)
177  |       g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
178  |     
179  |     g_string_sprintfa(query, " last WRITE, history WRITE ");
180  |     
181  |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
182  | 
183  | //fprintf(stderr,"%s\n", query->str);
184  | 
185  | /* Commit the transaction for AUX and LEAF tables that may be affected (taken from object template) */
186  |   for (i=TAB_START; tables[tr->class_type][i] != NULL; i++) {
187  |  /* Delete old records from the tables */  
188  |     g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld AND thread_id=0 ", tables[tr->class_type][i], tr->object_id);
189  |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
190  | //    fprintf(stderr, "D: query (del old): %s\n", query->str);  
191  | 
192  |  /* Set thread_id to 0 to commit the transaction */    
193  |     g_string_sprintf(query, "UPDATE %s SET thread_id=0 WHERE object_id=%ld", tables[tr->class_type][i], tr->object_id);
194  |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
195  | //    fprintf(stderr, "D: query (com new): %s\n", query->str);
196  |   }
197  |   
198  | /* Commit the transaction for the MAIN tables */
199  | 
200  | /* Commit the transaction for person_role, mntner, as_set, route_set tables */
201  | /* They require different handling because of dummies */
202  | /* The rule is: Update: dummy->0, Insert: preserve dummy value */
203  | /* These tables do not require deletions since we cannot have such condition (object_id==0 AND thread_id==0) */
204  |  if((tr->class_type==C_PN) || (tr->class_type==C_RO) || 
205  |    (tr->class_type==C_AS) || (tr->class_type==C_RS) ||
206  |    (tr->class_type==C_MT)){
207  | 
208  |  /* Process the rows updated/touched */
209  |     g_string_sprintf(query, "UPDATE %s SET thread_id=0, dummy=0 WHERE object_id=%ld AND thread_id=%d ", tables[tr->class_type][0], tr->object_id, tr->thread_upd);
210  |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
211  | //    fprintf(stderr, "D: query (com new): %s\n", query->str);
212  |  }
213  |  
214  |   if((tr->class_type==C_IR) && (tr->save)){ // Some special processing for inet_rtr - not good, but...
215  |    /* Delete old records from the table */  
216  |     g_string_sprintf(query, "DELETE FROM inet_rtr WHERE thread_id=0 AND object_id=%ld", tr->object_id);
217  |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
218  | //    fprintf(stderr, "D: query (del old): %s\n", query->str);  
219  | 
220  |     g_string_sprintf(query, "UPDATE inet_rtr SET thread_id=0, local_as='%s' WHERE object_id=%ld", (char *)tr->save, tr->object_id);
221  |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
222  | //    fprintf(stderr, "D: query (com new): %s\n", query->str);
223  |   }
224  |   else {
225  |  /* Process all other MAIN tables for updates/inserts and person_role, mntner, as_set, route_set tables for rows inserts */
226  |     g_string_sprintf(query, "UPDATE %s SET thread_id=0 WHERE object_id=%ld AND thread_id>0", tables[tr->class_type][0], tr->object_id);
227  |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
228  | //    fprintf(stderr, "D: query (com new): %s\n", query->str);
229  |   }  
230  | 
231  | 
232  | /* for tables that might be affected by dummies */
233  |  for(j=0; j < tr->ndummy; j++)// if dummies have been created
234  |    for (i=1; tables[tr->class_type][i] != NULL; i++) {
235  |     g_string_sprintf(query, "UPDATE %s SET thread_id=0 WHERE object_id=%ld ", tables[tr->class_type][i], tr->dummy_id[j]);
236  |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
237  | //    fprintf(stderr, "D: query (com new DUMMY): %s\n", query->str);
238  |  }
239  | 
240  | // Update radix tree for route and inetnum
241  |  if(tr->standalone==0) { // only if server
242  |   if((tr->class_type==C_RT) && (tr->save)) {
243  |     if (RX_get_tree( &mytree, RIPE_REG, IP_V4, RX_FAM_RT) != RX_OK )err=-1;
244  |     else 
245  |       err=update_rx_bin(RX_OPER_CRE, mytree, (rx_bin_data_t *)tr->save, tr->object_id);
246  |   }
247  | 
248  |   if((tr->class_type==C_IN) && (tr->save)) {
249  |      if (RX_get_tree( &mytree, RIPE_REG, IP_V4, RX_FAM_IN) != RX_OK ) err=-1;
250  |      else
251  |        err=update_rx_inum(RX_OPER_CRE, mytree, (rx_inum_data_t *)tr->save, tr->object_id);
252  |   }
253  |  } 
254  |   
255  |  /* Unlock all tables */
256  |  g_string_sprintf(query, "UNLOCK TABLES ");
257  |  sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
258  | 
259  | //fprintf(stderr,"%s\n", query->str);
260  |   g_string_free(query, TRUE);
261  |   return(err);
262  | } /* commit() */
263  | 
264  | 
265  | /* Function to fill data for radix tree */
266  | static void get_rx_data(void *element_data, void *tr_ptr)
267  | {
268  | Attribute_t *attr = element_data;
269  | Transaction_t *tr = (Transaction_t *)tr_ptr;
270  | unsigned int prefix, prefix_length;
271  | unsigned int begin_in, end_in;
272  | static rx_bin_data_t rx_bin_data;
273  | static rx_inum_data_t rx_inum_data;
274  | 
275  |   switch(attr->type) {
276  |     case A_RT:
277  |     		expand_rt(attr->value, &prefix, &prefix_length);
278  |     		save_rx_pref(&rx_bin_data, prefix, prefix_length);
279  |     		tr->save = (void *)&rx_bin_data;
280  |     		break;
281  |     case A_IN:
282  |     		convert_in(attr->value, &begin_in, &end_in);
283  |     		save_rx_rang(&rx_inum_data, begin_in, end_in);
284  |     		tr->save = (void *)&rx_inum_data;
285  |     		break;
286  |     default:
287  |     		break;		
288  |   }
289  | }
290  | 
291  | /******************************************************
292  | *int delete(Transaction_t *tr)
293  | * Delete the object
294  | *
295  | *****************************************************/
296  | int delete(Transaction_t *tr) 
297  | {
298  | SQ_result_set_t * sql_result;
299  | rx_tree_t  *mytree;
300  | GString *query;
301  | int err=0;
302  | int i;
303  | int num;
304  | long sequence_id;
305  | long ref_id;
306  | long num_rec;
307  | long timestamp;
308  | 
309  | char sobject_id[STR_M];
310  | char *sql_str;
311  | 
312  |  
313  | /* Check for referential integrity of deletion */
314  | 
315  |    sprintf(sobject_id, "%ld", tr->object_id);
316  |    
317  |    switch(tr->class_type){
318  |     case C_PN:
319  |     case C_RO:
320  |         
321  |        if(tr->dummy==1)break;
322  |         
323  |        for (i=0; t_ipn[i] != NULL; i++) { 
324  |         sql_str= get_field_str(tr, "COUNT(*)", t_ipn[i], "pe_ro_id", sobject_id, NULL);
325  |         if(sql_str) {
326  |          num_rec = atol(sql_str);  free(sql_str);
327  |          ref_id=tr->object_id;
328  |          if(num_rec==1) {
329  |           sql_str= get_field_str(tr, "object_id", t_ipn[i], "pe_ro_id", sobject_id, NULL);
330  |           if(sql_str) {
331  |            ref_id = atol(sql_str);  free(sql_str);
332  |           } else {
333  |            tr->succeeded=0; tr->error |= ERROR_U_DBS; break;
334  |           }
335  |          }
336  |          if((num_rec>1) || (ref_id!=tr->object_id)) {
337  |            g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, t_ipn[i]);
338  |            tr->succeeded=0; tr->error |= ERROR_U_OBJ;
339  |          }
340  |         } else {
341  |          tr->succeeded=0; tr->error |= ERROR_U_DBS;
342  |         }
343  |        }   
344  |        break;
345  |         
346  |     case C_MT:
347  |     
348  |         if(tr->dummy==1)break;
349  |         
350  |        for (i=0; t_imt[i] != NULL; i++) { 
351  |         sql_str= get_field_str(tr, "COUNT(*)", t_imt[i], "mnt_id", sobject_id, NULL);
352  |         if(sql_str) {
353  |          num_rec = atol(sql_str);  free(sql_str);
354  |          ref_id=tr->object_id;
355  |          if(num_rec==1) { 
356  |             sql_str= get_field_str(tr, "object_id", t_imt[i], "mnt_id", sobject_id, NULL);
357  |             if(sql_str) {
358  |               ref_id = atol(sql_str);  free(sql_str);
359  |             } else {
360  |               tr->succeeded=0; tr->error |= ERROR_U_DBS; break;
361  |             } 
362  |          }      
363  |          if((num_rec>1) || (ref_id!=tr->object_id)) {
364  |            g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, t_imt[i]);
365  |            tr->succeeded=0; tr->error |= ERROR_U_OBJ;
366  |          }
367  |         } else {
368  |          tr->succeeded=0; tr->error |= ERROR_U_DBS;
369  |         }
370  |        }   
371  |        break;
372  |         
373  |     case C_RS:
374  |     case C_AS:
375  |         sql_str= get_field_str(tr, "COUNT(*)", "member_of", "set_id", sobject_id, NULL);
376  |         if(sql_str) {
377  |          num_rec = atol(sql_str);  free(sql_str);
378  |          ref_id=tr->object_id;
379  |          if(num_rec==1) {
380  |           sql_str= get_field_str(tr, "object_id", "member_of", "set_id", sobject_id, NULL);
381  |           if(sql_str) {
382  |            ref_id = atol(sql_str);  free(sql_str);
383  |           } else {
384  |            tr->succeeded=0; tr->error |= ERROR_U_DBS; break;
385  |           }
386  |          }
387  |          if((num_rec>1) || (ref_id!=tr->object_id)) {
388  |            g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, "member_of");
389  |            tr->succeeded=0; tr->error |= ERROR_U_OBJ;
390  |          }
391  |         } else {
392  |          tr->succeeded=0; tr->error |= ERROR_U_DBS;
393  |         }
394  |         break;
395  | 
396  |     default:
397  |         break;    
398  |    } 
399  |    
400  |    if(tr->succeeded==0){
401  |     return(-1);
402  |    }
403  |    
404  | 	
405  |  if ((query = g_string_sized_new(STR_XXL)) == NULL){ 
406  |    fprintf(stderr, "E: cannot allocate gstring\n");
407  |    tr->succeeded=0;
408  |    tr->error|=ERROR_U_MEM;
409  |    return(ERROR_U_MEM); 
410  |  }
411  |    
412  | 
413  | /* Lock all relevant tables */
414  |     if(tr->class_type==C_IR) g_string_sprintf(query, "LOCK TABLES inet_rtr WRITE"); 
415  |     else g_string_sprintf(query, "LOCK TABLES %s WRITE,", tables[tr->class_type][0]);
416  |     
417  |     for (i=1; tables[tr->class_type][i] != NULL; i++) 
418  |       g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
419  |     
420  |     for (i=TAB_START; tables[tr->class_type][i] != NULL; i++)
421  |       g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
422  |     
423  |     g_string_sprintfa(query, " last WRITE, history WRITE ");
424  |     
425  |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
426  | 
427  | //fprintf(stderr,"%s\n", query->str);
428  | 
429  |     for (i=TAB_START; tables[tr->class_type][i] != NULL; i++) {
430  |     g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld ", tables[tr->class_type][i], tr->object_id);
431  |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
432  | //    fprintf(stderr, "D: query (delete): %s\n", query->str);
433  |   }
434  | 
435  | /* process tables differently for these type of objects (because of dummy). Table should appear first */
436  |  if((tr->class_type==C_PN) || (tr->class_type==C_RO) ||
437  |     (tr->class_type==C_AS) || (tr->class_type==C_RS) ||
438  |     (tr->class_type==C_MT)) {
439  |     g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld ", tables[tr->class_type][0], tr->object_id);
440  |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
441  | //    fprintf(stderr, "D: query (delete): %s\n", query->str);
442  |  }
443  | 
444  |   if(tr->class_type==C_IR){
445  | /* Don't forget about inet_rtr, since we haven't included it in the Tables. */
446  |     g_string_sprintf(query, "DELETE FROM inet_rtr WHERE  object_id=%ld ", tr->object_id);
447  |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
448  | //    fprintf(stderr, "D: query (delete): %s\n", query->str);
449  |   }
450  |   else {
451  | /* Process the MAIN tables (they appear first in the table list */
452  |     g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld ", tables[tr->class_type][0], tr->object_id);
453  |     sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
454  | //    fprintf(stderr, "D: query (delete): %s\n", query->str);
455  |   }  
456  | 
457  | 
458  |   g_string_sprintf(query, 	"INSERT history "
459  | 				"SELECT 0, object_id, sequence_id, timestamp, object_type, object "
460  |        				"FROM last "
461  |        				"WHERE object_id=%ld ", tr->object_id);
462  | 
463  | //fprintf(stderr, "D: copying entry into the history table\n %s\n", query->str); 
464  |       
465  |   sql_result=SQ_execute_query(SQ_STORE, tr->sql_connection, query->str);
466  |   num = mysql_affected_rows(tr->sql_connection);
467  |   if (sql_result)SQ_free_result(sql_result);
468  |   if ((num == -1) || (num == 0)) {
469  |          fprintf(stderr, "E ERROR!<perform_update>: INSERT history failed:[%d][%s]\n", num, query->str);
470  |          tr->succeeded=0;
471  |          tr->error |=ERROR_U_DBS;
472  |   }
473  | 
474  |   // get sequence number
475  |   sequence_id = get_sequence_id(tr);
476  |        
477  |   // insert new version into the last
478  |   timestamp=time(NULL);
479  |   
480  |     g_string_sprintf(query, "UPDATE last SET object='' WHERE object_id=%ld ", tr->object_id);
481  | 
482  |   sql_result=SQ_execute_query(SQ_STORE, tr->sql_connection, query->str);
483  |   num = mysql_affected_rows(tr->sql_connection);
484  |   if (sql_result)SQ_free_result(sql_result);
485  |   if ((num == -1) || (num == 0)) {
486  |   	fprintf(stderr, "E ERROR!<perform_update>: UPDATE last failed: [%d][%s]\n", num, query->str);
487  |          tr->succeeded=0;
488  |          tr->error |= ERROR_U_DBS;
489  |   }
490  | 
491  | 
492  |  // Do more in the forest
493  |  // Update radix tree for route and inetnum
494  |  if(tr->standalone==0) { // only if server
495  |   g_slist_foreach((tr->object)->attributes, get_rx_data, tr);
496  |   if((tr->class_type==C_RT) && (tr->save)) {
497  |     if (RX_get_tree( &mytree, RIPE_REG, IP_V4, RX_FAM_RT) != RX_OK ) err=-1;
498  |     else err=update_rx_bin(RX_OPER_DEL, mytree, (rx_bin_data_t *)tr->save, tr->object_id);
499  |   }
500  | 
501  |   if((tr->class_type==C_IN) && (tr->save)) {
502  |     if (RX_get_tree( &mytree, RIPE_REG, IP_V4, RX_FAM_IN) != RX_OK ) err=-1;
503  |     else err=update_rx_inum(RX_OPER_DEL, mytree, (rx_inum_data_t *)tr->save, tr->object_id);
504  |   }
505  |  } 
506  | 
507  |  /* Unlock all tables */
508  |  g_string_sprintf(query, "UNLOCK TABLES ");
509  |  sql_result=SQ_execute_query(SQ_NOSTORE, tr->sql_connection, query->str);
510  | 
511  | //fprintf(stderr,"%s\n", query->str);
512  | 
513  |   g_string_free(query, TRUE);
514  | 
515  |   return(err);
516  | 
517  | } /* delete() */