Source File ud/ud_comrol.c

    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  |