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 |