1    | /***************************************
2    |   $Revision: 1.32 $
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,2001,2002                    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   | 
34   | #include "rip.h"
35   | #include "ud_comrol.h"
36   | #include "syntax_api.h"
37   | 
38   | 
39   | /************************************************************
40   | * int UD_rollback()                                         *
41   | *                                                           *
42   | * Rolls back the transaction                                *
43   | *                                                           *
44   | * It locks all relevant tables and processes the rollback   *
45   | * General approach is to delete all new records related     *
46   | * to the transaction (thread_id==thread_ins) and clean up   *
47   | * old ones (thread_id==thread_upd)                          *
48   | *                                                           *
49   | ************************************************************/
50   |  
51   | int UD_rollback(Transaction_t *tr) {
52   | int i, j;
53   | int sql_err;
54   | 
55   |  if(ACT_DELETE(tr->action)) return(0);
56   | 	
57   | 
58   | /* Lock all relevant tables */
59   |    g_string_sprintf(tr->query, "LOCK TABLES ");
60   |    
61   |    /* we need to lock tables for mntner and role differently, otherwise there will be duplicates (names names, etc.)*/
62   |    if((tr->class_type!=C_MT) && (tr->class_type!=C_RO)){
63   |     g_string_sprintfa(tr->query, " %s WRITE,",  DF_get_class_sql_table(tr->class_type));
64   |     
65   |     for (i=0; tables[tr->class_type][i] != NULL; i++) 
66   |       g_string_sprintfa(tr->query, " %s WRITE,", tables[tr->class_type][i]);
67   |    } else { /* mntner and role are special cases */
68   |       g_string_sprintfa(tr->query, " mntner WRITE, person_role WRITE, ");
69   |    }
70   |    
71   |     for (i=TAB_START; tables[tr->class_type][i] != NULL; i++)
72   |       g_string_sprintfa(tr->query, " %s WRITE,", tables[tr->class_type][i]);
73   |     
74   |     g_string_sprintfa(tr->query, " last WRITE, history WRITE ");
75   |     
76   |     sql_err=SQ_execute_query(tr->sql_connection, tr->query->str, NULL);
77   | 
78   | /* Process AUX and LEAF tables */
79   |     for (i=TAB_START; tables[tr->class_type][i] != NULL; i++) {
80   |     /* Delete what has been inserted */
81   |     g_string_sprintf(tr->query, "DELETE FROM %s WHERE object_id=%ld AND thread_id=%d", tables[tr->class_type][i], tr->object_id, tr->thread_ins);
82   |     sql_err=SQ_execute_query(tr->sql_connection, tr->query->str, NULL);
83   | 
84   |     /* Normalize what has been updated/touched */
85   |     g_string_sprintf(tr->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);
86   |     sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
87   |   }
88   | 
89   | /* Process MAIN tables */
90   | /* Delete if a record was created */
91   |     g_string_sprintf(tr->query, "DELETE FROM %s WHERE  object_id=%ld AND thread_id=%d", 
92   |                              DF_get_class_sql_table(tr->class_type), tr->object_id, tr->thread_ins);
93   |     sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
94   |     
95   |     /* This is needed only for objects with possible dummy type, as they are updated with TR_UPDATE */
96   |     /* We use this tag when committing the update to set dummy==0 */
97   |     /* XXX may be later this should be reconsidered */
98   |     g_string_sprintf(tr->query, "UPDATE %s SET thread_id=0 WHERE  object_id=%ld AND thread_id=%d", 
99   |                              DF_get_class_sql_table(tr->class_type), tr->object_id, tr->thread_upd);
100  |     sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
101  | 
102  | /* Now tables  that might be affected by dummies */
103  |     for(j=0; j < tr->ndummy; j++) 
104  |     for (i=0; tables[tr->class_type][i] != NULL; i++) {
105  |     	g_string_sprintf(tr->query, "DELETE FROM %s WHERE object_id=%ld ", tables[tr->class_type][i], tr->dummy_id[j]);
106  |     	sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
107  |     } 
108  | 
109  |   /* if dummies have been created - get rid of them */
110  |   for(j=0; j < tr->ndummy; j++){
111  | 	 g_string_sprintf(tr->query, "DELETE FROM last WHERE object_id=%ld ", tr->dummy_id[j]);
112  | 	 sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
113  |   }
114  |   
115  | /* Rollback last and history tables */
116  | 
117  |     /* Delete what has been inserted */
118  |     g_string_sprintf(tr->query, "DELETE FROM history WHERE object_id=%ld AND thread_id=%d", tr->object_id, tr->thread_ins);
119  |     sql_err=SQ_execute_query(tr->sql_connection, tr->query->str, NULL);
120  | 
121  |     /* Normalize what has been updated/touched */
122  |     g_string_sprintf(tr->query, "UPDATE history SET thread_id=0 WHERE object_id=%ld AND thread_id=%d", tr->object_id, tr->thread_upd);
123  |     sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
124  | 
125  |     /* Delete what has been inserted */
126  |     g_string_sprintf(tr->query, "DELETE FROM last WHERE object_id=%ld AND thread_id=%d", tr->object_id, tr->thread_ins);
127  |     sql_err=SQ_execute_query(tr->sql_connection, tr->query->str, NULL);
128  | 
129  |     /* Normalize what has been updated/touched */
130  |     g_string_sprintf(tr->query, "UPDATE last SET thread_id=0 WHERE object_id=%ld AND thread_id=%d", tr->object_id, tr->thread_upd);
131  |     sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
132  | 
133  |   
134  |   /* Unlock all tables */
135  |   g_string_sprintf(tr->query, "UNLOCK TABLES ");
136  |   sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
137  | 
138  |   return(0);
139  | } /* rollback() */
140  | 
141  | /************************************************************
142  | * int UD_commit_I()                                         *
143  | *                                                           *
144  | * Performs I phase of the commit - deletions                *
145  | *                                                           *
146  | * General approach is to delete untouched rec (thread_id==0)*
147  | *                                                           *
148  | ************************************************************/
149  | 
150  | int UD_commit_I(Transaction_t *tr) {
151  | int err=0;
152  | int i;
153  | int sql_err;
154  | 
155  | 
156  | 
157  | /* Commit the transaction for AUX and LEAF tables that may be affected (taken from object template) */
158  |   for (i=TAB_START; tables[tr->class_type][i] != NULL; i++) {
159  |  /* Delete old records from the tables */  
160  |     g_string_sprintf(tr->query, "DELETE FROM %s WHERE object_id=%ld AND thread_id=0 ", tables[tr->class_type][i], tr->object_id);
161  |     sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
162  |     /*    ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s query (del old): %s\n", UD_TAG, tr->query->str);  */
163  |   }
164  | 
165  |  /* Delete old record from the last table */  
166  |     g_string_sprintf(tr->query, "DELETE FROM last WHERE object_id=%ld AND thread_id=0 ", tr->object_id);
167  |     sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
168  |     /*    ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s query (del old): %s\n", UD_TAG, tr->query->str);  */
169  | 
170  |   
171  |  return(err); 	
172  | }
173  | 
174  | /************************************************************
175  | * int UD_commit_II()                                        *
176  | *                                                           *
177  | * Performs I phase of the commit - deletions                *
178  | * General approach is to clean up all new and updated       *
179  | * records related to the transaction                        *
180  | * (thread_id==thread_ins) and (thread_id==thread_upd)       *
181  | *                                                           *
182  | ************************************************************/
183  | int UD_commit_II(Transaction_t *tr) {
184  | int err=0;
185  | int i,j;
186  | A_Type_t attr_type;
187  | int sql_err;
188  | 
189  |  
190  | /* Commit the transaction for AUX and LEAF tables that may be affected (taken from object template) */
191  |   for (i=TAB_START; tables[tr->class_type][i] != NULL; i++) {
192  |  /* Set thread_id to 0 to commit the transaction */    
193  |     g_string_sprintf(tr->query, "UPDATE %s SET thread_id=0 WHERE object_id=%ld", tables[tr->class_type][i], tr->object_id);
194  |     sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
195  |     /*    ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s query (com new): %s\n", UD_TAG, tr->query->str); */
196  |   }
197  |   
198  | /* Commit changes to the last table */  
199  |    g_string_sprintf(tr->query, "UPDATE last SET thread_id=0 WHERE object_id=%ld ", tr->object_id);
200  |    sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
201  | 
202  | /* Commit changes to the history table */  
203  |    g_string_sprintf(tr->query, "UPDATE history SET thread_id=0 WHERE object_id=%ld ", tr->object_id);
204  |    sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
205  |    
206  | /* Commit the transaction for the MAIN tables */
207  | 
208  | /* Commit the transaction for person_role, mntner, as_set, route_set tables */
209  | /* They require different handling because of dummies */
210  | /* The rule is: Update: dummy->0, Insert: preserve dummy value */
211  | /* These tables do not require deletions since we cannot have such condition (object_id==0 AND thread_id==0) */
212  |  if((tr->class_type==C_PN) || (tr->class_type==C_RO) || 
213  |    (tr->class_type==C_AS)  || (tr->class_type==C_RS) ||
214  |    (tr->class_type==C_MT)  || (tr->class_type==C_IT)){
215  | 
216  |  /* Process the rows updated/touched */
217  |     g_string_sprintf(tr->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);
218  |     sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
219  |  }
220  |  
221  |  switch (tr->class_type) {
222  |    case C_IR:
223  |    case C_IN:
224  |    case C_I6:
225  |    case C_FS:
226  | /*    if((tr->save)){ */
227  |     /* Some special processing for tables with the second attribute */
228  |      /* Update the second field of the table with query like one below */
229  |      /* UPDATE %s SET thread_id=%d, local_as='%s' WHERE object_id=%ld */
230  | 
231  |      /* XXX if second attribute is missing - make it empty */
232  |      if(tr->save==NULL) tr->save=g_strdup("");
233  |      switch(tr->class_type) {
234  |       /* Local-as for inet-rtr */
235  |       case C_IR: attr_type=A_LA;
236  |                  break;
237  |       /* netname for inetnum and inet6num */           
238  |       case C_IN: 
239  |       case C_I6: attr_type=A_NA;
240  |                  break;
241  |       /* filter for filter-set */           
242  |       case C_FS: attr_type=A_FI;
243  |                  break;
244  |       default:
245  | 		 ER_perror(FAC_UD, UD_BUG, "not valid class type\n");
246  |                  attr_type=A_END;
247  |                  die;
248  |      }
249  |      g_string_sprintf(tr->query, DF_get_update_query(attr_type), DF_get_class_sql_table(tr->class_type), 0, (char *)tr->save, tr->object_id);
250  |      sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
251  | /*    }
252  |     else {
253  |      ER_perror(FAC_UD, UD_BUG, "second attribute is not saved\n");
254  |      die;
255  |     }
256  | */    
257  |     break;
258  |    
259  |    default:  
260  |  /* Process all other MAIN tables for updates/inserts and person_role, mntner, as_set, route_set tables for rows inserts */
261  |     g_string_sprintf(tr->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);
262  |     sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
263  |     break;
264  |  }  
265  | 
266  | 
267  | /* for tables that might be affected by dummies */
268  |  for(j=0; j < tr->ndummy; j++)/* if dummies have been created */
269  |    for (i=0; tables[tr->class_type][i] != NULL; i++) {
270  |     g_string_sprintf(tr->query, "UPDATE %s SET thread_id=0 WHERE object_id=%ld ", tables[tr->class_type][i], tr->dummy_id[j]);
271  |     sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
272  |  }
273  | 
274  | 
275  |    for(j=0; j < tr->ndummy; j++){/* if dummies have been created*/
276  | 	 g_string_sprintf(tr->query, "UPDATE last SET thread_id=0 WHERE object_id=%ld ", tr->dummy_id[j]);
277  | 	 sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
278  |   }
279  |  
280  |  return(err);		
281  | }
282  | 
283  | 
284  | /************************************************************
285  | * int UD_commit()                                           *
286  | *                                                           *
287  | * Commits the transaction                                   *
288  | *                                                           *
289  | * It locks all relevant tables and processes the 2 phases of*
290  | * commit. It also performs checkpointing of phases and      * 
291  | * radix tree update                                         * 
292  | *                                                           * 
293  | * We need to split commit into 2 because otherwise it is    *
294  | * hard to distinguish between commited records and untouched*
295  | * ones (both have thread_id==0). Splitting and checkpointing*
296  | * solves this problem                                       *
297  | *                                                           *
298  | ************************************************************/
299  | 
300  | int UD_commit(Transaction_t *tr) {
301  | int err=0;
302  | int i;
303  | int sql_err;
304  | 
305  | if(ACT_DELETE(tr->action)) return(0);
306  | 
307  | 
308  | /* Lock all relevant tables */
309  |    g_string_sprintf(tr->query, "LOCK TABLES ");
310  |    
311  |    /* we need to lock tables for mntner and role differently, otherwise there will be duplicates (names names, etc.)*/
312  | /*   if((tr->class_type!=C_MT) && (tr->class_type!=C_RO)){ */
313  |    g_string_sprintfa(tr->query, " %s WRITE,",  DF_get_class_sql_table(tr->class_type));
314  |    
315  |    if((tr->class_type==C_RO)) g_string_sprintfa(tr->query, " mntner WRITE, ");
316  |    else if((tr->class_type==C_MT)) g_string_sprintfa(tr->query, " person_role WRITE, names WRITE, "); 
317  |     else
318  |      for (i=0; tables[tr->class_type][i] != NULL; i++) 
319  |         g_string_sprintfa(tr->query, " %s WRITE,", tables[tr->class_type][i]);
320  |    
321  |    for (i=TAB_START; tables[tr->class_type][i] != NULL; i++)
322  |       g_string_sprintfa(tr->query, " %s WRITE,", tables[tr->class_type][i]);
323  |     
324  |    g_string_sprintfa(tr->query, " last WRITE, history WRITE, transaction_rec WRITE ");
325  |     
326  |    sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
327  | 
328  | 
329  |   /* Perform first phase - deletions */
330  |   UD_commit_I(tr);
331  |   /* checkpoint this step */
332  |   CP_COMMIT_I_PASSED(tr->action); TR_update_status(tr);
333  |   /* Perform first phase - updates */
334  |   UD_commit_II(tr);
335  |   /* checkpoint this step */
336  |   CP_COMMIT_II_PASSED(tr->action); TR_update_status(tr);
337  |   
338  |  /* Unlock all tables */
339  |  g_string_sprintf(tr->query, "UNLOCK TABLES ");
340  |  sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
341  | 
342  |  /* Update radix tree for route, inetnum and inaddr-arpa domain*/
343  |  err = UD_update_rx(tr, RX_OPER_CRE);
344  |  
345  |  return(err);
346  | } /* commit() */
347  | 
348  | /************************************************************
349  | * int UD_check_ref()                                        *
350  | *                                                           *
351  | * Checks if the object to be deleted is referenced from     *
352  | * anywhere                                                  *
353  | *                                                           *
354  | * 0 - go ahead                                              *
355  | * -1 - deletion will compromise ref.integrity               *
356  | * Result is also reflected in tr->succeeded                 *
357  | ************************************************************/
358  | int UD_check_ref(Transaction_t *tr) 
359  | {
360  | int i;
361  | long ref_id;
362  | long num_rec;
363  | 
364  | char sobject_id[STR_M];
365  | char *sql_str;
366  | 
367  | 
368  | /* Check for referential integrity of deletion */
369  | 
370  |    sprintf(sobject_id, "%ld", tr->object_id);
371  | 
372  |    switch(tr->class_type){
373  |     case C_PN:
374  |     case C_RO:
375  |         
376  |        /* Check that this person/role object is not referenced */
377  |         
378  |        for (i=0; t_ipn[i] != NULL; i++) { 
379  |         /* Calculate number of references */
380  |         sql_str= get_field_str(tr->sql_connection, "COUNT(*)", t_ipn[i], "pe_ro_id", sobject_id, NULL);
381  |         if(sql_str) {
382  |          num_rec = atol(sql_str);  UT_free(sql_str);
383  |          ref_id=tr->object_id;
384  |          /* Check if it is a self reference (for role objects) */
385  |          if(num_rec==1) {
386  |           sql_str= get_field_str(tr->sql_connection, "object_id", t_ipn[i], "pe_ro_id", sobject_id, NULL);
387  |           if(sql_str) {
388  |            ref_id = atol(sql_str);  UT_free(sql_str);
389  |           } else {
390  | 	   /* this is not possible unless it is an sql error */
391  | 	   /*XXX probably we need to die */	  
392  |            tr->succeeded=0; tr->error |= ERROR_U_DBS; break;
393  |           }
394  |          }
395  |          /* If there are references (and not the only self reference) we cannot delete */
396  |          if((num_rec>1) || (ref_id!=tr->object_id)) {
397  |            g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, t_ipn[i]);
398  |            tr->succeeded=0; tr->error |= ERROR_U_OBJ;
399  |          }
400  |         } else {
401  |         /* SQL error occured */
402  |          tr->succeeded=0; tr->error |= ERROR_U_DBS;
403  |          g_string_sprintfa(tr->error_script,"E[%d][%s]:%s\n", ERROR_U_DBS, t_ipn[i], SQ_error(tr->sql_connection));
404  |         }
405  |        }
406  |        
407  |        /* Check that this person/role object is not referenced by name (legacy stuff) */
408  |        /* But allow overriding this check in NRTM mode and with override_integrity    */
409  |        if(IS_DUMMY_ALLOWED(tr->mode))break;
410  |        else { /*XXX legacy stuff */
411  |       
412  |         /* get the splitted words of the name (done in ud_split_names()) and compose the full name */
413  |         /* then compare this reconstructed value with the stored legacy "nic-handle" */
414  |         GList *person, *p;
415  |         GString *reconstructed_value;
416  | 
417  |         if (tr->class_type == C_PN) 
418  |            person = rpsl_object_get_attr(tr->object, "person");
419  |         else
420  |            person = rpsl_object_get_attr(tr->object, "role");
421  |            
422  |         reconstructed_value = g_string_new(rpsl_attr_get_value(person->data));
423  | 
424  |         for (p = g_list_next(person); p!=NULL; p = g_list_next(p)) {
425  |           g_string_sprintfa(reconstructed_value, " %s", rpsl_attr_get_value(p->data));
426  |         }
427  |         rpsl_attr_delete_list(person);
428  |         
429  |         for (i=0; t_ipn[i] != NULL; i++) { 
430  |         /* Calculate number of references */
431  |         
432  |          g_string_sprintf(tr->query, "SELECT COUNT(*) FROM %s, person_role "
433  |                                 "WHERE person_role.object_id=%s.pe_ro_id "
434  |                                 "AND person_role.nic_hdl='%s' ", t_ipn[i], t_ipn[i], reconstructed_value->str);
435  |         
436  | 
437  |          sql_str= get_qresult_str(tr->sql_connection, tr->query->str);
438  | 
439  |          if(sql_str) {
440  |           num_rec = atol(sql_str);  UT_free(sql_str);
441  |           /* If there are references (no self reference is possible in this case) we cannot delete */
442  |           if(num_rec>0) {
443  |            g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, t_ipn[i]);
444  |            tr->succeeded=0; tr->error |= ERROR_U_OBJ;
445  |           }
446  |          } else {
447  |          /* SQL error occured */
448  |           tr->succeeded=0; tr->error |= ERROR_U_DBS;
449  |           g_string_sprintfa(tr->error_script,"E[%d][%s]:%s\n", ERROR_U_DBS, t_ipn[i], SQ_error(tr->sql_connection));
450  |          }
451  |         }
452  |         g_string_free(reconstructed_value, TRUE);
453  | 
454  |        }   
455  |        break;
456  |         
457  |     case C_MT:
458  |     
459  |         /* Check that this mntner object is not referenced */
460  |         
461  |        for (i=0; t_imt[i] != NULL; i++) { 
462  |        /* Calculate number of references */
463  |         sql_str= get_field_str(tr->sql_connection, "COUNT(*)", t_imt[i], "mnt_id", sobject_id, NULL);
464  |         if(sql_str) {
465  |          num_rec = atol(sql_str);  UT_free(sql_str);
466  |          ref_id=tr->object_id;
467  |          /* Check if it is a self reference  */
468  |          if(num_rec==1) { 
469  |             sql_str= get_field_str(tr->sql_connection, "object_id", t_imt[i], "mnt_id", sobject_id, NULL);
470  |             if(sql_str) {
471  |               ref_id = atol(sql_str);  UT_free(sql_str);
472  |             } else {
473  |               tr->succeeded=0; tr->error |= ERROR_U_DBS; break;
474  |             } 
475  |          }
476  |          /* If there are references (and not the only self reference) we cannot delete */ 
477  |          if((num_rec>1) || (ref_id!=tr->object_id)) {
478  |            g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, t_imt[i]);
479  |            tr->succeeded=0; tr->error |= ERROR_U_OBJ;
480  |          }
481  |         } else {
482  |          tr->succeeded=0; tr->error |= ERROR_U_DBS;
483  |         }
484  |        }   
485  |        break;
486  | 
487  |     case C_IT:
488  |     
489  |         /* Check that this irt object is not referenced */
490  |         
491  |        for (i=0; t_iit[i] != NULL; i++) { 
492  |        /* Calculate number of references */
493  |         sql_str= get_field_str(tr->sql_connection, "COUNT(*)", t_iit[i], "irt_id", sobject_id, NULL);
494  |         if(sql_str) {
495  |          num_rec = atol(sql_str);  UT_free(sql_str);
496  |          ref_id=tr->object_id;
497  |          /* Check if it is a self reference  */
498  |          /* IRT object cannot have self references */
499  |          
500  |          /* If there are references (and not the only self reference) we cannot delete */ 
501  |          if(num_rec>0) {
502  |            g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, t_iit[i]);
503  |            tr->succeeded=0; tr->error |= ERROR_U_OBJ;
504  |          }
505  |         } else {
506  |          tr->succeeded=0; tr->error |= ERROR_U_DBS;
507  |         }
508  |        }   
509  |        break;
510  |         
511  |     case C_RS:
512  |     case C_AS:
513  |         /* Check that this set object is not referenced */
514  |         /* Calculate number of references */
515  |         sql_str= get_field_str(tr->sql_connection, "COUNT(*)", "member_of", "set_id", sobject_id, NULL);
516  |         if(sql_str) {
517  |          num_rec = atol(sql_str);  UT_free(sql_str);
518  |          /* XXX though set may contain other sets as memebers, */
519  |          /* there is no member-of attribute in these objects. */
520  |          /* So no self-reference is possible */
521  |          if(num_rec!=0) {
522  |            g_string_sprintfa(tr->error_script,"I[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, "member_of");
523  | 	  /* XXX Do not refuse the transaction but change the object to dummy */
524  |            tr->action |=TA_DUMMY;
525  |          }
526  |         } else {
527  |          tr->succeeded=0; tr->error |= ERROR_U_DBS;
528  |         }
529  |         break;
530  | 
531  |     default:
532  |         break;    
533  |    } 
534  |    
535  |  /* Check if we have passed referential integrity check */  
536  |  if(tr->succeeded) return(0); else return(-1);
537  |  
538  | } 
539  | 	
540  | /************************************************************
541  | * int UD_delete()                                              *
542  | *                                                           *
543  | * Deletes the object                                        *
544  | *                                                           *
545  | * It deletes the object from all relevant tables. 
546  | * Then it updates the radix tree for routes, inetnums 
547  | * and rev.domains           *
548  | *                                                           *
549  | ************************************************************/
550  | int UD_delete(Transaction_t *tr) 
551  | {
552  | int err=0;
553  | int i;
554  | long timestamp;
555  | int sql_err;
556  | int ref_set;
557  | 
558  | /* if we are deliting referenced set, we need to  perform delete a bit differently */
559  | /* no deletions of aux tables */
560  | /* dummy main, instead of del */
561  | /* dummy last instead of empty */
562  | /* So let's determine if we are deliting referenced set */
563  | if ((tr->class_type==C_AS || tr->class_type==C_RS) && ACT_UPD_DUMMY(tr->action)) ref_set = 1; else ref_set = 0;
564  | 
565  | /* Lock all relevant tables */
566  |    g_string_sprintf(tr->query, "LOCK TABLES ");
567  |    
568  |    /* we need to lock tables for mntner and role differently, otherwise there will be duplicates (names names, etc.)*/
569  |    if((tr->class_type!=C_MT) && (tr->class_type!=C_RO)){
570  |     g_string_sprintfa(tr->query, " %s WRITE,",  DF_get_class_sql_table(tr->class_type));
571  |     
572  |     for (i=0; tables[tr->class_type][i] != NULL; i++) 
573  |       g_string_sprintfa(tr->query, " %s WRITE,", tables[tr->class_type][i]);
574  |    } else { /* mntner and role are special cases */
575  |       g_string_sprintfa(tr->query, " mntner WRITE, person_role WRITE, ");
576  |    }
577  |    
578  |     for (i=TAB_START; tables[tr->class_type][i] != NULL; i++)
579  |       g_string_sprintfa(tr->query, " %s WRITE,", tables[tr->class_type][i]);
580  |     
581  |     g_string_sprintfa(tr->query, " last WRITE, history WRITE ");
582  |     
583  |     sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
584  |     if (sql_err) {
585  | 	 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), tr->query->str);
586  |          tr->succeeded=0;
587  |          tr->error |=ERROR_U_DBS;
588  | 	 die;
589  |     }
590  | /* Update the history table */
591  | /* XXX Crash recovery: */
592  | /* If history was not updated - we will create a record */
593  | /* If history was already updated but last wasn't - we will just replace the record */
594  | /* If history and last were already updated - we will have an empty query - 0 rows should be affected */
595  |     g_string_sprintf(tr->query, "REPLACE history "
596  | 				"SELECT 0, object_id, sequence_id, timestamp, object_type, object, pkey, serial, prev_serial "
597  |        				"FROM last "
598  |        				"WHERE object_id=%ld AND sequence_id=%ld ", tr->object_id, tr->sequence_id);
599  |     sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
600  |     if (sql_err) {
601  | 	 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), tr->query->str);
602  |          tr->succeeded=0;
603  |          tr->error |=ERROR_U_DBS;
604  | 	 die;
605  |     }
606  | 
607  | /* Delete records from the leaf and aux tables */
608  |     for (i=TAB_START; tables[tr->class_type][i] != NULL; i++) {
609  |      g_string_sprintf(tr->query, "DELETE FROM %s WHERE object_id=%ld ", tables[tr->class_type][i], tr->object_id);
610  |      sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
611  |     /*    ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s query (delete): %s\n", UD_TAG, tr->query->str);*/
612  |        if (sql_err) {
613  | 	 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), tr->query->str);
614  |          tr->succeeded=0;
615  |          tr->error |=ERROR_U_DBS;
616  | 	 die;
617  |        }
618  |     }  
619  |      
620  | 
621  |  /* For all object except as-sets and route-sets we need to empty MAIN table */
622  |  /* For referenced sets, however, we transform them to dummy, not delete */
623  |  if (ref_set == 0) {
624  | 
625  | /* Process the MAIN table  */
626  |     g_string_sprintf(tr->query, "DELETE FROM %s WHERE object_id=%ld ", DF_get_class_sql_table(tr->class_type), tr->object_id);
627  |    
628  | 
629  |     sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
630  |     if (sql_err) {
631  | 	 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), tr->query->str);
632  |          tr->succeeded=0;
633  |          tr->error |=ERROR_U_DBS;
634  | 	 die;
635  |     }
636  |  
637  |  } else { /* this is the referenced set */
638  |  /* we need to 'dummy' MAIN */
639  |     g_string_sprintf(tr->query, "UPDATE %s SET dummy=1 WHERE object_id=%ld ", DF_get_class_sql_table(tr->class_type), tr->object_id);
640  |                
641  |     sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
642  |     if (sql_err) {
643  |          ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), tr->query->str);
644  |          tr->succeeded=0;
645  |          tr->error |= ERROR_U_DBS;
646  |          die;
647  |     }
648  |  }
649  |        
650  |   /* insert new version into the last */
651  |   timestamp=time(NULL);
652  |   
653  |  if(ref_set == 0) 
654  |  {
655  |  /* empty the contents, but leave in the table to restrict re-use of object_id */ 
656  |  /* XXX change sequence_id=0 so it is easy to say that the object was deleted */
657  |   g_string_sprintf(tr->query, "UPDATE last SET object='', timestamp=%ld, sequence_id=0  WHERE object_id=%ld ", timestamp, tr->object_id);
658  | 
659  |   sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
660  |   if (sql_err) {
661  | 	 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), tr->query->str);
662  |          tr->succeeded=0;
663  |          tr->error |= ERROR_U_DBS;
664  | 	 die;
665  |   }
666  |  } else {/* this is the referenced set */
667  |  /* 'dummy' the contents, but leave in the table to prevent re-use of object_id */ 
668  |  g_string_sprintf(tr->query, "UPDATE last SET object='DUMMY SET', object_type=%d, sequence_id=%ld, timestamp=%ld  WHERE object_id=%ld ", DUMMY_TYPE, tr->sequence_id+1, timestamp, tr->object_id);
669  | 
670  |  sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
671  |  if (sql_err) {
672  |      ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), tr->query->str);
673  |      tr->succeeded=0;
674  |      tr->error |= ERROR_U_DBS;
675  |     die;
676  |    }
677  |  }
678  | 
679  | 
680  |  /* Unlock all tables */
681  |   g_string_sprintf(tr->query, "UNLOCK TABLES ");
682  |   sql_err = SQ_execute_query(tr->sql_connection, tr->query->str, (SQ_result_set_t **)NULL);
683  |   if (sql_err) {
684  | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), tr->query->str);
685  |         tr->succeeded=0;
686  |         tr->error |= ERROR_U_DBS;
687  | 	die;
688  |   }
689  | 
690  |   return(err);
691  | 
692  | } /* delete() */ 
693  | 
694  | 
695  | 
696  |      	       /* Do more in the forest
697  |    * Update radix tree for route and inetnum
698  |    */
699  | 
700  | int UD_update_rx(Transaction_t *tr, rx_oper_mt mode)
701  | {
702  | rp_upd_pack_t *packptr = tr->packptr;
703  | int err=0;
704  | 
705  |   
706  |   if(!IS_STANDALONE(tr->mode)) { /* only if server */
707  |   
708  | 
709  |     /* Only for these types of objects and only if we have collected data (tr->save != NULL) */
710  |     if( (   (tr->class_type==C_RT) 
711  | 	 || (tr->class_type==C_IN) 
712  | 	 || (tr->class_type==C_I6)
713  | 	 || (tr->class_type==C_DN))) {
714  |       /* Collect some data for radix tree and NH repository update for deletes*/
715  |       if(mode == RX_OPER_DEL)g_list_foreach((GList *)rpsl_object_get_all_attr(tr->object), get_rx_data, tr);
716  |       
717  |       /* Except for regular domains we need to update radix tree */
718  |       if(ACT_UPD_RX(tr->action)){
719  |        packptr->key = tr->object_id;
720  |        if( RP_pack_node(mode, packptr, tr->source_hdl) == RX_OK ) {
721  | 	err = 0;
722  |        } else {
723  | 	err = (-1);
724  | 	ER_perror(FAC_UD, UD_BUG, "cannot update radix tree\n");
725  | 	die;
726  |        }
727  |       } /* update radix tree */
728  |     }
729  |   }
730  |   return(err);
731  | }
732  |    
733  | 	       
734  |