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 |