1 | /*************************************** 2 | $Revision: 1.26 $ 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 | if((tr->class_type==C_RO)) g_string_sprintfa(query, " mntner WRITE, "); 348 | else if((tr->class_type==C_MT)) g_string_sprintfa(query, " person_role WRITE, names WRITE, "); 349 | else 350 | for (i=0; tables[tr->class_type][i] != NULL; i++) 351 | g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]); 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 | 398 | char sobject_id[STR_M]; 399 | char *sql_str; 400 | 401 | /* Try to allocate g_string. Return on error */ 402 | if ((query = g_string_sized_new(STR_XXL)) == NULL){ 403 | ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n"); 404 | tr->succeeded=0; 405 | tr->error|=ERROR_U_MEM; 406 | die; 407 | } 408 | 409 | 410 | /* Check for referential integrity of deletion */ 411 | 412 | sprintf(sobject_id, "%ld", tr->object_id); 413 | 414 | switch(tr->class_type){ 415 | case C_PN: 416 | case C_RO: 417 | 418 | /* Check that this person/role object is not referenced */ 419 | 420 | for (i=0; t_ipn[i] != NULL; i++) { 421 | /* Calculate number of references */ 422 | sql_str= get_field_str(tr->sql_connection, "COUNT(*)", t_ipn[i], "pe_ro_id", sobject_id, NULL); 423 | if(sql_str) { 424 | num_rec = atol(sql_str); free(sql_str); 425 | ref_id=tr->object_id; 426 | /* Check if it is a self reference (for role objects) */ 427 | if(num_rec==1) { 428 | sql_str= get_field_str(tr->sql_connection, "object_id", t_ipn[i], "pe_ro_id", sobject_id, NULL); 429 | if(sql_str) { 430 | ref_id = atol(sql_str); free(sql_str); 431 | } else { 432 | tr->succeeded=0; tr->error |= ERROR_U_DBS; break; 433 | } 434 | } 435 | /* If there are references (and not the only self reference) we cannot delete */ 436 | if((num_rec>1) || (ref_id!=tr->object_id)) { 437 | g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, t_ipn[i]); 438 | tr->succeeded=0; tr->error |= ERROR_U_OBJ; 439 | } 440 | } else { 441 | /* SQL error occured */ 442 | tr->succeeded=0; tr->error |= ERROR_U_DBS; 443 | g_string_sprintfa(tr->error_script,"E[%d][%s]:%s\n", ERROR_U_DBS, t_ipn[i], SQ_error(tr->sql_connection)); 444 | } 445 | } 446 | 447 | /* Check that this person/role object is not referenced by name (legacy stuff) */ 448 | /* But allow overriding this check in NRTM mode and with override_integrity */ 449 | if(IS_DUMMY_ALLOWED(tr->mode))break; 450 | 451 | for (i=0; t_ipn[i] != NULL; i++) { 452 | /* Calculate number of references */ 453 | 454 | g_string_sprintf(query, "SELECT COUNT(*) FROM %s, person_role " 455 | "WHERE person_role.object_id=%s.pe_ro_id " 456 | "AND person_role.nic_hdl='%s' ", t_ipn[i], t_ipn[i], tr->save); 457 | 458 | sql_str= get_qresult_str(tr->sql_connection, query->str); 459 | if(sql_str) { 460 | num_rec = atol(sql_str); free(sql_str); 461 | /* If there are references (no self reference is possible in this case) we cannot delete */ 462 | if(num_rec>0) { 463 | g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, t_ipn[i]); 464 | tr->succeeded=0; tr->error |= ERROR_U_OBJ; 465 | } 466 | } else { 467 | /* SQL error occured */ 468 | tr->succeeded=0; tr->error |= ERROR_U_DBS; 469 | g_string_sprintfa(tr->error_script,"E[%d][%s]:%s\n", ERROR_U_DBS, t_ipn[i], SQ_error(tr->sql_connection)); 470 | } 471 | } 472 | 473 | break; 474 | 475 | case C_MT: 476 | 477 | /* Check that this mntner object is not referenced */ 478 | 479 | for (i=0; t_imt[i] != NULL; i++) { 480 | /* Calculate number of references */ 481 | sql_str= get_field_str(tr->sql_connection, "COUNT(*)", t_imt[i], "mnt_id", sobject_id, NULL); 482 | if(sql_str) { 483 | num_rec = atol(sql_str); free(sql_str); 484 | ref_id=tr->object_id; 485 | /* Check if it is a self reference */ 486 | if(num_rec==1) { 487 | sql_str= get_field_str(tr->sql_connection, "object_id", t_imt[i], "mnt_id", sobject_id, NULL); 488 | if(sql_str) { 489 | ref_id = atol(sql_str); free(sql_str); 490 | } else { 491 | tr->succeeded=0; tr->error |= ERROR_U_DBS; break; 492 | } 493 | } 494 | /* If there are references (and not the only self reference) we cannot delete */ 495 | if((num_rec>1) || (ref_id!=tr->object_id)) { 496 | g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, t_imt[i]); 497 | tr->succeeded=0; tr->error |= ERROR_U_OBJ; 498 | } 499 | } else { 500 | tr->succeeded=0; tr->error |= ERROR_U_DBS; 501 | } 502 | } 503 | break; 504 | 505 | case C_RS: 506 | case C_AS: 507 | /* Check that this set object is not referenced */ 508 | /* Calculate number of references */ 509 | sql_str= get_field_str(tr->sql_connection, "COUNT(*)", "member_of", "set_id", sobject_id, NULL); 510 | if(sql_str) { 511 | num_rec = atol(sql_str); free(sql_str); 512 | /* XXX though set may contain other sets as memebers, */ 513 | /* there is no member-of attribute in these objects. */ 514 | /* So no self-reference is possible */ 515 | if(num_rec!=0) { 516 | g_string_sprintfa(tr->error_script,"I[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, "member_of"); 517 | /* XXX Do not refuse the transaction but change the object to dummy */ 518 | tr->action |=TA_DUMMY; 519 | } 520 | } else { 521 | tr->succeeded=0; tr->error |= ERROR_U_DBS; 522 | } 523 | break; 524 | 525 | default: 526 | break; 527 | } 528 | 529 | g_string_free(query, TRUE); 530 | 531 | /* Check if we have passed referential integrity check */ 532 | if(tr->succeeded) return(0); else return(-1); 533 | 534 | } 535 | 536 | /************************************************************ 537 | * int UD_delete() * 538 | * * 539 | * Deletes the object * 540 | * * 541 | * It deletes the object from all relevant tables. 542 | * Then it updates the radix tree for routes, inetnums 543 | * and rev.domains * 544 | * * 545 | ************************************************************/ 546 | int UD_delete(Transaction_t *tr) 547 | { 548 | GString *query; 549 | int err=0; 550 | int i; 551 | long timestamp; 552 | int sql_err; 553 | int ref_set; 554 | 555 | /* if we are deliting referenced set, we need to perform delete a bit differently */ 556 | /* no deletions of aux tables */ 557 | /* dummy main, instead of del */ 558 | /* dummy last instead of empty */ 559 | /* So let's determine if we are deliting referenced set */ 560 | if ((tr->class_type==C_AS || tr->class_type==C_RS) && ACT_UPD_DUMMY(tr->action)) ref_set = 1; else ref_set = 0; 561 | 562 | /* Try to allocate g_string. Return on error */ 563 | if ((query = g_string_sized_new(STR_XXL)) == NULL){ 564 | ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n"); 565 | tr->succeeded=0; 566 | tr->error|=ERROR_U_MEM; 567 | die; 568 | } 569 | 570 | 571 | /* Lock all relevant tables */ 572 | g_string_sprintf(query, "LOCK TABLES "); 573 | 574 | /* we need to lock tables for mntner and role differently, otherwise there will be duplicates (names names, etc.)*/ 575 | if((tr->class_type!=C_MT) && (tr->class_type!=C_RO)){ 576 | g_string_sprintfa(query, " %s WRITE,", DF_get_class_sql_table(tr->class_type)); 577 | 578 | for (i=0; tables[tr->class_type][i] != NULL; i++) 579 | g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]); 580 | } else { /* mntner and role are special cases */ 581 | g_string_sprintfa(query, " mntner WRITE, person_role WRITE, "); 582 | } 583 | 584 | for (i=TAB_START; tables[tr->class_type][i] != NULL; i++) 585 | g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]); 586 | 587 | g_string_sprintfa(query, " last WRITE, history WRITE "); 588 | 589 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL); 590 | if (sql_err) { 591 | ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str); 592 | tr->succeeded=0; 593 | tr->error |=ERROR_U_DBS; 594 | die; 595 | } 596 | /* Update the history table */ 597 | /* XXX Crash recovery: */ 598 | /* If history was not updated - we will create a record */ 599 | /* If history was already updated but last wasn't - we will just replace the record */ 600 | /* If history and last were already updated - we will have an empty query - 0 rows should be affected */ 601 | g_string_sprintf(query, "REPLACE history " 602 | "SELECT 0, object_id, sequence_id, timestamp, object_type, object, pkey, serial, prev_serial " 603 | "FROM last " 604 | "WHERE object_id=%ld AND sequence_id=%ld ", tr->object_id, tr->sequence_id); 605 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL); 606 | if (sql_err) { 607 | ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str); 608 | tr->succeeded=0; 609 | tr->error |=ERROR_U_DBS; 610 | die; 611 | } 612 | 613 | /* Delete records from the leaf and aux tables */ 614 | for (i=TAB_START; tables[tr->class_type][i] != NULL; i++) { 615 | g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld ", tables[tr->class_type][i], tr->object_id); 616 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL); 617 | /* ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s query (delete): %s\n", UD_TAG, query->str);*/ 618 | if (sql_err) { 619 | ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str); 620 | tr->succeeded=0; 621 | tr->error |=ERROR_U_DBS; 622 | die; 623 | } 624 | } 625 | 626 | 627 | /* For all object except as-sets and route-sets we need to empty MAIN table */ 628 | /* For referenced sets, however, we transform them to dummy, not delete */ 629 | if (ref_set == 0) { 630 | 631 | /* Process the MAIN table */ 632 | g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld ", DF_get_class_sql_table(tr->class_type), tr->object_id); 633 | 634 | 635 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL); 636 | if (sql_err) { 637 | ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str); 638 | tr->succeeded=0; 639 | tr->error |=ERROR_U_DBS; 640 | die; 641 | } 642 | 643 | } else { /* this is the referenced set */ 644 | /* we need to 'dummy' MAIN */ 645 | g_string_sprintf(query, "UPDATE %s SET dummy=1 WHERE object_id=%ld ", DF_get_class_sql_table(tr->class_type), tr->object_id); 646 | 647 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL); 648 | if (sql_err) { 649 | ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str); 650 | tr->succeeded=0; 651 | tr->error |= ERROR_U_DBS; 652 | die; 653 | } 654 | } 655 | 656 | /* insert new version into the last */ 657 | timestamp=time(NULL); 658 | 659 | if(ref_set == 0) 660 | { 661 | /* empty the contents, but leave in the table to restrict re-use of object_id */ 662 | /* XXX change sequence_id=0 so it is easy to say that the object was deleted */ 663 | g_string_sprintf(query, "UPDATE last SET object='', timestamp=%ld, sequence_id=0 WHERE object_id=%ld ", timestamp, tr->object_id); 664 | 665 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL); 666 | if (sql_err) { 667 | ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str); 668 | tr->succeeded=0; 669 | tr->error |= ERROR_U_DBS; 670 | die; 671 | } 672 | } else {/* this is the referenced set */ 673 | /* 'dummy' the contents, but leave in the table to prevent re-use of object_id */ 674 | 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); 675 | 676 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL); 677 | if (sql_err) { 678 | ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str); 679 | tr->succeeded=0; 680 | tr->error |= ERROR_U_DBS; 681 | die; 682 | } 683 | } 684 | 685 | 686 | /* Unlock all tables */ 687 | g_string_sprintf(query, "UNLOCK TABLES "); 688 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL); 689 | if (sql_err) { 690 | ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str); 691 | tr->succeeded=0; 692 | tr->error |= ERROR_U_DBS; 693 | die; 694 | } 695 | 696 | 697 | g_string_free(query, TRUE); 698 | 699 | return(err); 700 | 701 | } /* delete() */ 702 | 703 | 704 | 705 | /* Do more in the forest 706 | * Update radix tree for route and inetnum 707 | */ 708 | 709 | int UD_update_rx(Transaction_t *tr, rx_oper_mt mode) 710 | { 711 | rp_upd_pack_t *packptr = tr->packptr; 712 | int err=0; 713 | 714 | 715 | if(!IS_STANDALONE(tr->mode)) { /* only if server */ 716 | 717 | 718 | /* Only for these types of objects and only if we have collected data (tr->save != NULL) */ 719 | if( ( (tr->class_type==C_RT) 720 | || (tr->class_type==C_IN) 721 | || (tr->class_type==C_I6) 722 | || (tr->class_type==C_DN))) { 723 | /* Collect some data for radix tree and NH repository update for deletes*/ 724 | if(mode == RX_OPER_DEL)g_slist_foreach((tr->object)->attributes, get_rx_data, tr); 725 | 726 | /* Except for regular domains we need to update radix tree */ 727 | if(ACT_UPD_RX(tr->action)){ 728 | packptr->key = tr->object_id; 729 | if( RP_pack_node(mode, packptr, tr->source_hdl) == RX_OK ) { 730 | err = 0; 731 | } else { 732 | err = (-1); 733 | ER_perror(FAC_UD, UD_BUG, "cannot update radix tree\n"); 734 | die; 735 | } 736 | } /* update radix tree */ 737 | } 738 | } 739 | return(err); 740 | } 741 | 742 | 743 |