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 |