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