1    | /***************************************
2    |   $Revision: 1.13 $
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   | #include "rp.h"
37   | 
38   | #define RIPE_REG 17
39   | 
40   | /************************************************************
41   | * int rollback(Transaction_t *tr)
42   | * Rollback the transaction
43   | *
44   | * **********************************************************/ 
45   | int rollback(Transaction_t *tr) {
46   | GString *query;
47   | long sequence_id;
48   | int i, j;
49   | int sql_err;
50   | 
51   |  if(ACT_DELETE(tr->action)) return(0);
52   | 	
53   |  if ((query = g_string_sized_new(STR_XXL)) == NULL){ 
54   |    fprintf(stderr, "E: cannot allocate gstring\n"); 
55   |    tr->succeeded=0;
56   |    tr->error |= ERROR_U_MEM;
57   |    return(ERROR_U_MEM); }
58   | 
59   | /* Lock all relevant tables */
60   |    g_string_sprintf(query, "LOCK TABLES %s WRITE,",  DF_get_class_sql_table(tr->class_type));
61   |     
62   |     for (i=0; 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_err=SQ_execute_query(tr->sql_connection, query->str, NULL);
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_err=SQ_execute_query(tr->sql_connection, query->str, NULL);
80   | 
81   |     /* Normalize what has been updated/touched */
82   |     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);
83   |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
84   |   }
85   | 
86   | /* Process MAIN tables */
87   |     g_string_sprintf(query, "DELETE FROM %s WHERE  object_id=%ld AND thread_id=%d", 
88   |                              DF_get_class_sql_table(tr->class_type), tr->object_id, tr->thread_ins);
89   |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
90   |     
91   |     /* This is needed only for objects with dummies, as they are updated with TR_UPDATE */
92   |     /* We use this tag when commiting the update to set dummy==0 */
93   |     /* XXX may be later this should be reconsidered */
94   |     g_string_sprintf(query, "UPDATE %s SET thread_id=0 WHERE  object_id=%ld AND thread_id=%d", 
95   |                              DF_get_class_sql_table(tr->class_type), tr->object_id, tr->thread_upd);
96   |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
97   | 
98   | /* Now tables  that might be affected by dummies */
99   |     for(j=0; j < tr->ndummy; j++) 
100  |     for (i=0; tables[tr->class_type][i] != NULL; i++) {
101  |     	g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld ", tables[tr->class_type][i], tr->dummy_id[j]);
102  |     	sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
103  |     } 
104  | 
105  | /* Rollback last and history tables */
106  |     if(ACT_UPDATE(tr->action)) { /* so we are updating an object */
107  |    g_string_sprintf(query, "DELETE FROM history WHERE object_id=%ld AND sequence_id=%ld", tr->object_id, tr->sequence_id-1);
108  |    sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
109  |    /* we do not need to delete a row in the last for updates    */
110  |   }
111  |     else { /* we failed to create an object */
112  |       sequence_id=1; /* sequence start == 1 */
113  |    g_string_sprintf(query, "DELETE FROM last WHERE object_id=%ld AND sequence_id=%ld", tr->object_id, sequence_id);
114  |    sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
115  |   }
116  | 
117  | 
118  |     for(j=0; j < tr->ndummy; j++){/* if dummies have been created */
119  | 	 g_string_sprintf(query, "DELETE FROM last WHERE object_id=%ld ", tr->dummy_id[j]);
120  | 	 sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
121  |   }
122  |   
123  |   /* Unlock all tables */
124  |   g_string_sprintf(query, "UNLOCK TABLES ");
125  |   sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
126  | 
127  |   
128  |   g_string_free(query, TRUE);
129  |   return(0);
130  | } /* rollback() */
131  | 
132  | /************************************************************
133  | * int commit(Transaction_t *tr)
134  | * Commit the transaction
135  | *
136  | * **********************************************************/ 
137  | int commit(Transaction_t *tr) {
138  | GString *query;
139  | int err=0;
140  | int i,j;
141  | A_Type_t attr_type;
142  | int sql_err;
143  | 
144  | if(ACT_DELETE(tr->action)) return(0);
145  | 
146  |  if ((query = g_string_sized_new(STR_XXL)) == NULL){ 
147  |    fprintf(stderr, "E: cannot allocate gstring\n"); 
148  |    tr->succeeded=0;
149  |    tr->error|=ERROR_U_MEM;
150  |    return(ERROR_U_MEM); 
151  |  }
152  | 
153  | /* Lock all relevant tables */
154  |     g_string_sprintf(query, "LOCK TABLES %s WRITE,", DF_get_class_sql_table(tr->class_type));
155  |     
156  |     for (i=0; tables[tr->class_type][i] != NULL; i++) 
157  |       g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
158  |     
159  |     for (i=TAB_START; tables[tr->class_type][i] != NULL; i++)
160  |       g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
161  |     
162  |     g_string_sprintfa(query, " last WRITE, history WRITE ");
163  |     
164  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
165  | 
166  | /* fprintf(stderr,"%s\n", query->str); */
167  | 
168  | /* Commit the transaction for AUX and LEAF tables that may be affected (taken from object template) */
169  |   for (i=TAB_START; tables[tr->class_type][i] != NULL; i++) {
170  |  /* Delete old records from the tables */  
171  |     g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld AND thread_id=0 ", tables[tr->class_type][i], tr->object_id);
172  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
173  |     /*    fprintf(stderr, "D: query (del old): %s\n", query->str);  */
174  | 
175  |  /* Set thread_id to 0 to commit the transaction */    
176  |     g_string_sprintf(query, "UPDATE %s SET thread_id=0 WHERE object_id=%ld", tables[tr->class_type][i], tr->object_id);
177  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
178  |     /*    fprintf(stderr, "D: query (com new): %s\n", query->str); */
179  |   }
180  |   
181  | /* Commit the transaction for the MAIN tables */
182  | 
183  | /* Commit the transaction for person_role, mntner, as_set, route_set tables */
184  | /* They require different handling because of dummies */
185  | /* The rule is: Update: dummy->0, Insert: preserve dummy value */
186  | /* These tables do not require deletions since we cannot have such condition (object_id==0 AND thread_id==0) */
187  |  if((tr->class_type==C_PN) || (tr->class_type==C_RO) || 
188  |    (tr->class_type==C_AS) || (tr->class_type==C_RS) ||
189  |    (tr->class_type==C_MT)){
190  | 
191  |  /* Process the rows updated/touched */
192  |     g_string_sprintf(query, "UPDATE %s SET thread_id=0, dummy=0 WHERE object_id=%ld AND thread_id=%d ",  DF_get_class_sql_table(tr->class_type), tr->object_id, tr->thread_upd);
193  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
194  |  }
195  |  
196  |  switch (tr->class_type) {
197  |    case C_IR:
198  |    case C_IN:
199  |    case C_I6:
200  |    case C_FS: 
201  |     if((tr->save)){ /* Some special processing for tables with the second attribute */
202  |      /* Update the second field of the table with query like one below */
203  |      /* UPDATE %s SET thread_id=%d, local_as='%s' WHERE object_id=%ld */
204  |      
205  |      switch(tr->class_type) {
206  |       /* Local-as for inet-rtr */
207  |       case C_IR: attr_type=A_LA;
208  |                  break;
209  |       /* netname for inetnum and inet6num */           
210  |       case C_IN: 
211  |       case C_I6: attr_type=A_NA;
212  |                  break;
213  |       /* filter for filter-set */           
214  |       case C_FS: attr_type=A_FI;
215  |                  break;
216  |       default:
217  |                  die;
218  |                  break;           
219  |      }
220  |      g_string_sprintf(query, DF_get_update_query(attr_type), DF_get_class_sql_table(tr->class_type), 0, (char *)tr->save, tr->object_id);
221  |      sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
222  |     }
223  |     else die;
224  |     break;
225  |    
226  |    default:  
227  |  /* Process all other MAIN tables for updates/inserts and person_role, mntner, as_set, route_set tables for rows inserts */
228  |     g_string_sprintf(query, "UPDATE %s SET thread_id=0 WHERE object_id=%ld AND thread_id>0", DF_get_class_sql_table(tr->class_type), tr->object_id);
229  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
230  |     break;
231  |  }  
232  | 
233  | 
234  | /* for tables that might be affected by dummies */
235  |  for(j=0; j < tr->ndummy; j++)/* if dummies have been created */
236  |    for (i=0; tables[tr->class_type][i] != NULL; i++) {
237  |     g_string_sprintf(query, "UPDATE %s SET thread_id=0 WHERE object_id=%ld ", tables[tr->class_type][i], tr->dummy_id[j]);
238  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
239  |  }
240  | 
241  | 
242  |    for(j=0; j < tr->ndummy; j++){/* if dummies have been created*/
243  | 	 g_string_sprintf(query, "UPDATE last SET thread_id=0 WHERE object_id=%ld ", tr->dummy_id[j]);
244  | 	 sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
245  |   }
246  |   
247  |  /* Unlock all tables */
248  |  g_string_sprintf(query, "UNLOCK TABLES ");
249  |  sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
250  | 
251  |  /* Update radix tree for route, inetnum and inaddr-arpa domain*/
252  |  if(tr->standalone==0) { /* only if server*/
253  |  
254  |  /* Create a radix node for the object */
255  |    if( (   (tr->class_type==C_RT) 
256  | 	|| (tr->class_type==C_IN) 
257  | 	|| (tr->class_type==C_I6)
258  | 	|| (tr->class_type==C_DN))
259  |        && (tr->packptr)) {
260  |      rp_upd_pack_t *packptr = tr->packptr;
261  |      
262  |      packptr->key = tr->object_id;
263  |      
264  |      if( RP_pack_node(RX_OPER_CRE, packptr, RIPE_REG) == RX_OK ) {
265  |        err = 0;
266  |      } else {
267  |        err = (-1) ;
268  |      }
269  |    }   
270  |    /* XXX Check for errors */
271  |  } 
272  | 
273  |   g_string_free(query, TRUE);
274  |   return(err);
275  | } /* commit() */
276  | 
277  | 
278  | 
279  | /******************************************************
280  | *int delete(Transaction_t *tr)
281  | * Delete the object
282  | *
283  | *****************************************************/
284  | int delete(Transaction_t *tr) 
285  | {
286  | GString *query;
287  | int err=0;
288  | int i;
289  | int num;
290  | long ref_id;
291  | long num_rec;
292  | long timestamp;
293  | 
294  | char sobject_id[STR_M];
295  | char *sql_str;
296  | int sql_err;
297  | 
298  | 
299  |  /* Try to allocate g_string. Return on error */	
300  |  if ((query = g_string_sized_new(STR_XXL)) == NULL){ 
301  |    fprintf(stderr, "E: cannot allocate gstring\n");
302  |    tr->succeeded=0;
303  |    tr->error|=ERROR_U_MEM;
304  |    return(ERROR_U_MEM); 
305  |  }
306  | 
307  | 
308  | /* Check for referential integrity of deletion */
309  | 
310  |    sprintf(sobject_id, "%ld", tr->object_id);
311  | 
312  |    switch(tr->class_type){
313  |     case C_PN:
314  |     case C_RO:
315  |         
316  |        if(tr->dummy==1)break; /* This is if the override exists */
317  |        
318  |        /* Check that this person/role object is not referenced */
319  |         
320  |        for (i=0; t_ipn[i] != NULL; i++) { 
321  |         /* Calculate number of references */
322  |         sql_str= get_field_str(tr->sql_connection, "COUNT(*)", t_ipn[i], "pe_ro_id", sobject_id, NULL);
323  |         if(sql_str) {
324  |          num_rec = atol(sql_str);  free(sql_str);
325  |          ref_id=tr->object_id;
326  |          /* Check if it is a self reference (for role objects) */
327  |          if(num_rec==1) {
328  |           sql_str= get_field_str(tr->sql_connection, "object_id", t_ipn[i], "pe_ro_id", sobject_id, NULL);
329  |           if(sql_str) {
330  |            ref_id = atol(sql_str);  free(sql_str);
331  |           } else {
332  |            tr->succeeded=0; tr->error |= ERROR_U_DBS; break;
333  |           }
334  |          }
335  |          /* If there are references (and not the only self reference) we cannot delete */
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  |         /* SQL error occured */
342  |          tr->succeeded=0; tr->error |= ERROR_U_DBS;
343  |          g_string_sprintfa(tr->error_script,"E[%d][%s]:%s\n", ERROR_U_DBS, t_ipn[i], SQ_error(tr->sql_connection));
344  |         }
345  |        }
346  |        
347  |        /* Check that this person/role object is not referenced by name (legacy stuff) */
348  |        
349  |        for (i=0; t_ipn[i] != NULL; i++) { 
350  |         /* Calculate number of references */
351  |         
352  |         g_string_sprintf(query, "SELECT COUNT(*) FROM %s, person_role "
353  |                                 "WHERE person_role.object_id=%s.pe_ro_id "
354  |                                 "AND person_role.nic_hdl='%s' ", t_ipn[i], t_ipn[i], tr->save);
355  |         
356  |         sql_str= get_qresult_str(tr->sql_connection, query->str);
357  |         if(sql_str) {
358  |          num_rec = atol(sql_str);  free(sql_str);
359  |          /* If there are references (no self reference is possible in this case) we cannot delete */
360  |          if(num_rec>0) {
361  |            g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, t_ipn[i]);
362  |            tr->succeeded=0; tr->error |= ERROR_U_OBJ;
363  |          }
364  |         } else {
365  |         /* SQL error occured */
366  |          tr->succeeded=0; tr->error |= ERROR_U_DBS;
367  |          g_string_sprintfa(tr->error_script,"E[%d][%s]:%s\n", ERROR_U_DBS, t_ipn[i], SQ_error(tr->sql_connection));
368  |         }
369  |        }
370  |           
371  |        break;
372  |         
373  |     case C_MT:
374  |     
375  |         if(tr->dummy==1)break; /* This is if the override exists */
376  |         
377  |         /* Check that this mntner object is not referenced */
378  |         
379  |        for (i=0; t_imt[i] != NULL; i++) { 
380  |        /* Calculate number of references */
381  |         sql_str= get_field_str(tr->sql_connection, "COUNT(*)", t_imt[i], "mnt_id", sobject_id, NULL);
382  |         if(sql_str) {
383  |          num_rec = atol(sql_str);  free(sql_str);
384  |          ref_id=tr->object_id;
385  |          /* Check if it is a self reference  */
386  |          if(num_rec==1) { 
387  |             sql_str= get_field_str(tr->sql_connection, "object_id", t_imt[i], "mnt_id", sobject_id, NULL);
388  |             if(sql_str) {
389  |               ref_id = atol(sql_str);  free(sql_str);
390  |             } else {
391  |               tr->succeeded=0; tr->error |= ERROR_U_DBS; break;
392  |             } 
393  |          }
394  |          /* If there are references (and not the only self reference) we cannot delete */ 
395  |          if((num_rec>1) || (ref_id!=tr->object_id)) {
396  |            g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, t_imt[i]);
397  |            tr->succeeded=0; tr->error |= ERROR_U_OBJ;
398  |          }
399  |         } else {
400  |          tr->succeeded=0; tr->error |= ERROR_U_DBS;
401  |         }
402  |        }   
403  |        break;
404  |         
405  |     case C_RS:
406  |     case C_AS:
407  |         /* Check that this set object is not referenced */
408  |         /* Calculate number of references */
409  |         sql_str= get_field_str(tr->sql_connection, "COUNT(*)", "member_of", "set_id", sobject_id, NULL);
410  |         if(sql_str) {
411  |          num_rec = atol(sql_str);  free(sql_str);
412  |          /* XXX though set may contain other sets as memebers, */
413  |          /* there is no member-of attribute in these objects. */
414  |          /* So no self-reference is possible */
415  |          if(num_rec!=0) {
416  |            g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, "member_of");
417  |            tr->succeeded=0; tr->error |= ERROR_U_OBJ;
418  | 	  /* XXX Do not refuse the transaction but change the object to dummy */
419  |          }
420  |         } else {
421  |          tr->succeeded=0; tr->error |= ERROR_U_DBS;
422  |         }
423  |         break;
424  | 
425  |     default:
426  |         break;    
427  |    } 
428  |    
429  |  /* Check if we have passed referential integrity check */  
430  |  if(tr->succeeded==0){
431  |        return(-1);
432  |  }
433  |           
434  | 
435  | /* Lock all relevant tables */
436  |     g_string_sprintf(query, "LOCK TABLES %s WRITE,", DF_get_class_sql_table(tr->class_type));
437  |     
438  |     for (i=0; tables[tr->class_type][i] != NULL; i++) 
439  |       g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
440  |     
441  |     for (i=TAB_START; tables[tr->class_type][i] != NULL; i++)
442  |       g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
443  |     
444  |     g_string_sprintfa(query, " last WRITE, history WRITE ");
445  |     
446  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
447  | 
448  |     for (i=TAB_START; tables[tr->class_type][i] != NULL; i++) {
449  |     g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld ", tables[tr->class_type][i], tr->object_id);
450  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
451  |     /*    fprintf(stderr, "D: query (delete): %s\n", query->str);*/
452  |   }
453  | 
454  | /* Process the MAIN table  */
455  |     g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld ", DF_get_class_sql_table(tr->class_type), tr->object_id);
456  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
457  | 
458  | /* Update the history table */
459  |     g_string_sprintf(query,     "INSERT history "
460  | 				"SELECT 0, object_id, sequence_id, timestamp, object_type, object "
461  |        				"FROM last "
462  |        				"WHERE object_id=%ld ", tr->object_id);
463  | 
464  |       
465  |   sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
466  |   if (sql_err) {
467  |          fprintf(stderr, "E ERROR!<perform_update>: INSERT history failed:[%d][%s]\n", num, query->str);
468  |          tr->succeeded=0;
469  |          tr->error |=ERROR_U_DBS;
470  |   }
471  | 
472  |   /* get sequence number */
473  |   tr->sequence_id = get_sequence_id(tr);
474  |   tr->sequence_id++;
475  |        
476  |   /* insert new version into the last */
477  |   timestamp=time(NULL);
478  |   
479  |  /* empty the contents, but leave in the table to restrict re-use of object_id */ 
480  |   g_string_sprintf(query, "UPDATE last SET object='', timestamp=%ld  WHERE object_id=%ld ", timestamp, tr->object_id);
481  | 
482  |   sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
483  |   if (sql_err) {
484  |   	fprintf(stderr, "E ERROR!<perform_update>: UPDATE last failed: [%d][%s]\n", num, query->str);
485  |          tr->succeeded=0;
486  |          tr->error |= ERROR_U_DBS;
487  |   }
488  | 
489  | 
490  |   /* Do more in the forest
491  |    * Update radix tree for route and inetnum
492  |    */
493  |   if(tr->standalone==0) { /* only if server */
494  |   /* Collect some data for radix tree and NH repository update */
495  |     g_slist_foreach((tr->object)->attributes, get_rx_data, tr);
496  | 
497  |     /* Only for these types of objects and only if we have collected data (tr->save != NULL) */
498  |     if( (   (tr->class_type==C_RT) 
499  | 	 || (tr->class_type==C_IN) 
500  | 	 || (tr->class_type==C_I6)
501  | 	 || (tr->class_type==C_DN))
502  | 	&& (tr->packptr)) {
503  |       rp_upd_pack_t *packptr = tr->packptr;
504  |       
505  |       packptr->key = tr->object_id;
506  |       if( RP_pack_node(RX_OPER_DEL, packptr, RIPE_REG) == RX_OK ) {
507  | 	err = 0;
508  |       } else {
509  | 	err = (-1) ;
510  |       }
511  |     }
512  |   }
513  |   
514  |  /* Unlock all tables */
515  |   g_string_sprintf(query, "UNLOCK TABLES ");
516  |   sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
517  | 
518  |   g_string_free(query, TRUE);
519  | 
520  |   return(err);
521  | 
522  | } /* delete() */