Source File modules/ud/ud_comrol.c

    1    | /***************************************
    2    |   $Revision: 1.23 $
    3    |
    4    |   rollback(), commit(), delete() - rollback, commit update transaction, delete an object
    5    |
    6    |   Status: NOT REVUED, NOT TESTED
    7    |
    8    |  Author(s):       Andrei Robachevsky
    9    |
    10   |   ******************/ /******************
    11   |   Modification History:
    12   |         andrei (17/01/2000) Created.
    13   |   ******************/ /******************
    14   |   Copyright (c) 2000                              RIPE NCC
    15   |
    16   |   All Rights Reserved
    17   |
    18   |   Permission to use, copy, modify, and distribute this software and its
    19   |   documentation for any purpose and without fee is hereby granted,
    20   |   provided that the above copyright notice appear in all copies and that
    21   |   both that copyright notice and this permission notice appear in
    22   |   supporting documentation, and that the name of the author not be
    23   |   used in advertising or publicity pertaining to distribution of the
    24   |   software without specific, written prior permission.
    25   |
    26   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
    27   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
    28   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
    29   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
    30   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    31   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    32   |  ***************************************/
    33   | #include "ud.h"
    34   | #include "ud_int.h"
    35   | #include "ud_comrol.h"
    36   | #include "ud_tr.h"
    37   | #include "rp.h"
    38   |
    39   |
    40   | /************************************************************
    41   | * int UD_rollback()                                         *
    42   | *                                                           *
    43   | * Rolls back the transaction                                *
    44   | *                                                           *
    45   | * It locks all relevant tables and processes the rollback   *
    46   | * General approach is to delete all new records related     *
    47   | * to the transaction (thread_id==thread_ins) and clean up   *
    48   | * old ones (thread_id==thread_upd)                          *
    49   | *                                                           *
    50   | ************************************************************/
    51   |
    52   | int UD_rollback(Transaction_t *tr) {
    53   | GString *query;
    54   | int i, j;
    55   | int sql_err;
    56   |
    57   |  if(ACT_DELETE(tr->action)) return(0);
    58   |
    59   |  if ((query = g_string_sized_new(STR_XXL)) == NULL){
    60   |    ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
    61   |    tr->succeeded=0;
    62   |    tr->error |= ERROR_U_MEM;
    63   |    die;
    64   |  }
    65   |
    66   | /* Lock all relevant tables */
    67   |    g_string_sprintf(query, "LOCK TABLES ");
    68   |
    69   |    /* we need to lock tables for mntner and role differently, otherwise there will be duplicates (names names, etc.)*/
    70   |    if((tr->class_type!=C_MT) && (tr->class_type!=C_RO)){
    71   |     g_string_sprintfa(query, " %s WRITE,",  DF_get_class_sql_table(tr->class_type));
    72   |
    73   |     for (i=0; tables[tr->class_type][i] != NULL; i++)
    74   |       g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
    75   |    } else { /* mntner and role are special cases */
    76   |       g_string_sprintfa(query, " mntner WRITE, person_role WRITE, ");
    77   |    }
    78   |
    79   |     for (i=TAB_START; tables[tr->class_type][i] != NULL; i++)
    80   |       g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
    81   |
    82   |     g_string_sprintfa(query, " last WRITE, history WRITE ");
    83   |
    84   |     sql_err=SQ_execute_query(tr->sql_connection, query->str, NULL);
    85   |
    86   | /* Process AUX and LEAF tables */
    87   |     for (i=TAB_START; tables[tr->class_type][i] != NULL; i++) {
    88   |     /* Delete what has been inserted */
    89   |     g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld AND thread_id=%d", tables[tr->class_type][i], tr->object_id, tr->thread_ins);
    90   |     sql_err=SQ_execute_query(tr->sql_connection, query->str, NULL);
    91   |
    92   |     /* Normalize what has been updated/touched */
    93   |     g_string_sprintf(query, "UPDATE %s SET thread_id=0 WHERE object_id=%ld AND thread_id=%d", tables[tr->class_type][i], tr->object_id, tr->thread_upd);
    94   |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    95   |   }
    96   |
    97   | /* Process MAIN tables */
    98   | /* Delete if a record was created */
    99   |     g_string_sprintf(query, "DELETE FROM %s WHERE  object_id=%ld AND thread_id=%d",
    100  |                              DF_get_class_sql_table(tr->class_type), tr->object_id, tr->thread_ins);
    101  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    102  |
    103  |     /* This is needed only for objects with possible dummy type, as they are updated with TR_UPDATE */
    104  |     /* We use this tag when committing the update to set dummy==0 */
    105  |     /* XXX may be later this should be reconsidered */
    106  |     g_string_sprintf(query, "UPDATE %s SET thread_id=0 WHERE  object_id=%ld AND thread_id=%d",
    107  |                              DF_get_class_sql_table(tr->class_type), tr->object_id, tr->thread_upd);
    108  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    109  |
    110  | /* Now tables  that might be affected by dummies */
    111  |     for(j=0; j < tr->ndummy; j++)
    112  |     for (i=0; tables[tr->class_type][i] != NULL; i++) {
    113  |     	g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld ", tables[tr->class_type][i], tr->dummy_id[j]);
    114  |     	sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    115  |     }
    116  |
    117  |   /* if dummies have been created - get rid of them */
    118  |   for(j=0; j < tr->ndummy; j++){
    119  | 	 g_string_sprintf(query, "DELETE FROM last WHERE object_id=%ld ", tr->dummy_id[j]);
    120  | 	 sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    121  |   }
    122  |
    123  | /* Rollback last and history tables */
    124  |
    125  |     /* Delete what has been inserted */
    126  |     g_string_sprintf(query, "DELETE FROM history WHERE object_id=%ld AND thread_id=%d", tr->object_id, tr->thread_ins);
    127  |     sql_err=SQ_execute_query(tr->sql_connection, query->str, NULL);
    128  |
    129  |     /* Normalize what has been updated/touched */
    130  |     g_string_sprintf(query, "UPDATE history SET thread_id=0 WHERE object_id=%ld AND thread_id=%d", tr->object_id, tr->thread_upd);
    131  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    132  |
    133  |     /* Delete what has been inserted */
    134  |     g_string_sprintf(query, "DELETE FROM last WHERE object_id=%ld AND thread_id=%d", tr->object_id, tr->thread_ins);
    135  |     sql_err=SQ_execute_query(tr->sql_connection, query->str, NULL);
    136  |
    137  |     /* Normalize what has been updated/touched */
    138  |     g_string_sprintf(query, "UPDATE last SET thread_id=0 WHERE object_id=%ld AND thread_id=%d", tr->object_id, tr->thread_upd);
    139  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    140  |
    141  |
    142  |   /* Unlock all tables */
    143  |   g_string_sprintf(query, "UNLOCK TABLES ");
    144  |   sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    145  |
    146  |
    147  |   g_string_free(query, TRUE);
    148  |   return(0);
    149  | } /* rollback() */
    150  |
    151  | /************************************************************
    152  | * int UD_commit_I()                                         *
    153  | *                                                           *
    154  | * Performs I phase of the commit - deletions                *
    155  | *                                                           *
    156  | * General approach is to delete untouched rec (thread_id==0)*
    157  | *                                                           *
    158  | ************************************************************/
    159  |
    160  | int UD_commit_I(Transaction_t *tr) {
    161  | GString *query;
    162  | int err=0;
    163  | int i;
    164  | int sql_err;
    165  |
    166  |
    167  |   if ((query = g_string_sized_new(STR_XXL)) == NULL){
    168  |    ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
    169  |    tr->succeeded=0;
    170  |    tr->error|=ERROR_U_MEM;
    171  |    die;
    172  |  }
    173  |
    174  | /* Commit the transaction for AUX and LEAF tables that may be affected (taken from object template) */
    175  |   for (i=TAB_START; tables[tr->class_type][i] != NULL; i++) {
    176  |  /* Delete old records from the tables */
    177  |     g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld AND thread_id=0 ", tables[tr->class_type][i], tr->object_id);
    178  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    179  |     /*    ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s query (del old): %s\n", UD_TAG, query->str);  */
    180  |   }
    181  |
    182  |  /* Delete old record from the last table */
    183  |     g_string_sprintf(query, "DELETE FROM last WHERE object_id=%ld AND thread_id=0 ", tr->object_id);
    184  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    185  |     /*    ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s query (del old): %s\n", UD_TAG, query->str);  */
    186  |
    187  |
    188  |  g_string_free(query, TRUE);
    189  |  return(err);
    190  | }
    191  |
    192  | /************************************************************
    193  | * int UD_commit_II()                                        *
    194  | *                                                           *
    195  | * Performs I phase of the commit - deletions                *
    196  | * General approach is to clean up all new and updated       *
    197  | * records related to the transaction                        *
    198  | * (thread_id==thread_ins) and (thread_id==thread_upd)       *
    199  | *                                                           *
    200  | ************************************************************/
    201  | int UD_commit_II(Transaction_t *tr) {
    202  | GString *query;
    203  | int err=0;
    204  | int i,j;
    205  | A_Type_t attr_type;
    206  | int sql_err;
    207  |
    208  |
    209  |  if ((query = g_string_sized_new(STR_XXL)) == NULL){
    210  |    ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
    211  |    tr->succeeded=0;
    212  |    tr->error|=ERROR_U_MEM;
    213  |    die;
    214  |  }
    215  |
    216  |
    217  | /* Commit the transaction for AUX and LEAF tables that may be affected (taken from object template) */
    218  |   for (i=TAB_START; tables[tr->class_type][i] != NULL; i++) {
    219  |  /* Set thread_id to 0 to commit the transaction */
    220  |     g_string_sprintf(query, "UPDATE %s SET thread_id=0 WHERE object_id=%ld", tables[tr->class_type][i], tr->object_id);
    221  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    222  |     /*    ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s query (com new): %s\n", UD_TAG, query->str); */
    223  |   }
    224  |
    225  | /* Commit changes to the last table */
    226  |    g_string_sprintf(query, "UPDATE last SET thread_id=0 WHERE object_id=%ld ", tr->object_id);
    227  |    sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    228  |
    229  | /* Commit changes to the history table */
    230  |    g_string_sprintf(query, "UPDATE history SET thread_id=0 WHERE object_id=%ld ", tr->object_id);
    231  |    sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    232  |
    233  | /* Commit the transaction for the MAIN tables */
    234  |
    235  | /* Commit the transaction for person_role, mntner, as_set, route_set tables */
    236  | /* They require different handling because of dummies */
    237  | /* The rule is: Update: dummy->0, Insert: preserve dummy value */
    238  | /* These tables do not require deletions since we cannot have such condition (object_id==0 AND thread_id==0) */
    239  |  if((tr->class_type==C_PN) || (tr->class_type==C_RO) ||
    240  |    (tr->class_type==C_AS) || (tr->class_type==C_RS) ||
    241  |    (tr->class_type==C_MT)){
    242  |
    243  |  /* Process the rows updated/touched */
    244  |     g_string_sprintf(query, "UPDATE %s SET thread_id=0, dummy=0 WHERE object_id=%ld AND thread_id=%d ",  DF_get_class_sql_table(tr->class_type), tr->object_id, tr->thread_upd);
    245  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    246  |  }
    247  |
    248  |  switch (tr->class_type) {
    249  |    case C_IR:
    250  |    case C_IN:
    251  |    case C_I6:
    252  |    case C_FS:
    253  |     if((tr->save)){ /* Some special processing for tables with the second attribute */
    254  |      /* Update the second field of the table with query like one below */
    255  |      /* UPDATE %s SET thread_id=%d, local_as='%s' WHERE object_id=%ld */
    256  |
    257  |      switch(tr->class_type) {
    258  |       /* Local-as for inet-rtr */
    259  |       case C_IR: attr_type=A_LA;
    260  |                  break;
    261  |       /* netname for inetnum and inet6num */
    262  |       case C_IN:
    263  |       case C_I6: attr_type=A_NA;
    264  |                  break;
    265  |       /* filter for filter-set */
    266  |       case C_FS: attr_type=A_FI;
    267  |                  break;
    268  |       default:
    269  | 		 ER_perror(FAC_UD, UD_BUG, "not valid class type\n");
    270  |                  die;
    271  |                  break;
    272  |      }
    273  |      g_string_sprintf(query, DF_get_update_query(attr_type), DF_get_class_sql_table(tr->class_type), 0, (char *)tr->save, tr->object_id);
    274  |      sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    275  |     }
    276  |     else {
    277  |      ER_perror(FAC_UD, UD_BUG, "second attribute is not saved\n");
    278  |      die;
    279  |     }
    280  |     break;
    281  |
    282  |    default:
    283  |  /* Process all other MAIN tables for updates/inserts and person_role, mntner, as_set, route_set tables for rows inserts */
    284  |     g_string_sprintf(query, "UPDATE %s SET thread_id=0 WHERE object_id=%ld AND thread_id>0", DF_get_class_sql_table(tr->class_type), tr->object_id);
    285  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    286  |     break;
    287  |  }
    288  |
    289  |
    290  | /* for tables that might be affected by dummies */
    291  |  for(j=0; j < tr->ndummy; j++)/* if dummies have been created */
    292  |    for (i=0; tables[tr->class_type][i] != NULL; i++) {
    293  |     g_string_sprintf(query, "UPDATE %s SET thread_id=0 WHERE object_id=%ld ", tables[tr->class_type][i], tr->dummy_id[j]);
    294  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    295  |  }
    296  |
    297  |
    298  |    for(j=0; j < tr->ndummy; j++){/* if dummies have been created*/
    299  | 	 g_string_sprintf(query, "UPDATE last SET thread_id=0 WHERE object_id=%ld ", tr->dummy_id[j]);
    300  | 	 sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    301  |   }
    302  |
    303  |  g_string_free(query, TRUE);
    304  |
    305  |  return(err);
    306  | }
    307  |
    308  |
    309  | /************************************************************
    310  | * int UD_commit()                                           *
    311  | *                                                           *
    312  | * Commits the transaction                                   *
    313  | *                                                           *
    314  | * It locks all relevant tables and processes the 2 phases of*
    315  | * commit. It also performs checkpointing of phases and      *
    316  | * radix tree update                                         *
    317  | *                                                           *
    318  | * We need to split commit into 2 because otherwise it is    *
    319  | * hard to distinguish between commited records and untouched*
    320  | * ones (both have thread_id==0). Splitting and checkpointing*
    321  | * solves this problem                                       *
    322  | *                                                           *
    323  | ************************************************************/
    324  |
    325  | int UD_commit(Transaction_t *tr) {
    326  | GString *query;
    327  | int err=0;
    328  | int i;
    329  | int sql_err;
    330  |
    331  | if(ACT_DELETE(tr->action)) return(0);
    332  |
    333  |  if ((query = g_string_sized_new(STR_XXL)) == NULL){
    334  |    ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
    335  |    tr->succeeded=0;
    336  |    tr->error|=ERROR_U_MEM;
    337  |    die;
    338  |  }
    339  |
    340  | /* Lock all relevant tables */
    341  |    g_string_sprintf(query, "LOCK TABLES ");
    342  |
    343  |    /* we need to lock tables for mntner and role differently, otherwise there will be duplicates (names names, etc.)*/
    344  |    if((tr->class_type!=C_MT) && (tr->class_type!=C_RO)){
    345  |     g_string_sprintfa(query, " %s WRITE,",  DF_get_class_sql_table(tr->class_type));
    346  |
    347  |     for (i=0; tables[tr->class_type][i] != NULL; i++)
    348  |       g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
    349  |    } else { /* mntner and role are special cases */
    350  |       g_string_sprintfa(query, " mntner WRITE, person_role WRITE, ");
    351  |    }
    352  |
    353  |     for (i=TAB_START; tables[tr->class_type][i] != NULL; i++)
    354  |       g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
    355  |
    356  |     g_string_sprintfa(query, " last WRITE, history WRITE, transaction_rec WRITE ");
    357  |
    358  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    359  |
    360  |
    361  |   /* Perform first phase - deletions */
    362  |   UD_commit_I(tr);
    363  |   /* checkpoint this step */
    364  |   CP_COMMIT_I_PASSED(tr->action); TR_update_status(tr);
    365  |   /* Perform first phase - updates */
    366  |   UD_commit_II(tr);
    367  |   /* checkpoint this step */
    368  |   CP_COMMIT_II_PASSED(tr->action); TR_update_status(tr);
    369  |
    370  |  /* Unlock all tables */
    371  |  g_string_sprintf(query, "UNLOCK TABLES ");
    372  |  sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    373  |
    374  |  /* Update radix tree for route, inetnum and inaddr-arpa domain*/
    375  |  err = UD_update_rx(tr, RX_OPER_CRE);
    376  |
    377  |  g_string_free(query, TRUE);
    378  |  return(err);
    379  | } /* commit() */
    380  |
    381  | /************************************************************
    382  | * int UD_check_ref()                                        *
    383  | *                                                           *
    384  | * Checks if the object to be deleted is referenced from     *
    385  | * anywhere                                                  *
    386  | *                                                           *
    387  | * 0 - go ahead                                              *
    388  | * -1 - deletion will compromise ref.integrity               *
    389  | * Result is also reflected in tr->succeeded                 *
    390  | ************************************************************/
    391  | int UD_check_ref(Transaction_t *tr)
    392  | {
    393  | GString *query;
    394  | int i;
    395  | long ref_id;
    396  | long num_rec;
    397  | long timestamp;
    398  |
    399  | char sobject_id[STR_M];
    400  | char *sql_str;
    401  | int sql_err;
    402  |
    403  |  /* Try to allocate g_string. Return on error */
    404  |  if ((query = g_string_sized_new(STR_XXL)) == NULL){
    405  |    ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
    406  |    tr->succeeded=0;
    407  |    tr->error|=ERROR_U_MEM;
    408  |    die;
    409  |  }
    410  |
    411  |
    412  | /* Check for referential integrity of deletion */
    413  |
    414  |    sprintf(sobject_id, "%ld", tr->object_id);
    415  |
    416  |    switch(tr->class_type){
    417  |     case C_PN:
    418  |     case C_RO:
    419  |
    420  |        /* Check that this person/role object is not referenced */
    421  |
    422  |        for (i=0; t_ipn[i] != NULL; i++) {
    423  |         /* Calculate number of references */
    424  |         sql_str= get_field_str(tr->sql_connection, "COUNT(*)", t_ipn[i], "pe_ro_id", sobject_id, NULL);
    425  |         if(sql_str) {
    426  |          num_rec = atol(sql_str);  free(sql_str);
    427  |          ref_id=tr->object_id;
    428  |          /* Check if it is a self reference (for role objects) */
    429  |          if(num_rec==1) {
    430  |           sql_str= get_field_str(tr->sql_connection, "object_id", t_ipn[i], "pe_ro_id", sobject_id, NULL);
    431  |           if(sql_str) {
    432  |            ref_id = atol(sql_str);  free(sql_str);
    433  |           } else {
    434  |            tr->succeeded=0; tr->error |= ERROR_U_DBS; break;
    435  |           }
    436  |          }
    437  |          /* If there are references (and not the only self reference) we cannot delete */
    438  |          if((num_rec>1) || (ref_id!=tr->object_id)) {
    439  |            g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, t_ipn[i]);
    440  |            tr->succeeded=0; tr->error |= ERROR_U_OBJ;
    441  |          }
    442  |         } else {
    443  |         /* SQL error occured */
    444  |          tr->succeeded=0; tr->error |= ERROR_U_DBS;
    445  |          g_string_sprintfa(tr->error_script,"E[%d][%s]:%s\n", ERROR_U_DBS, t_ipn[i], SQ_error(tr->sql_connection));
    446  |         }
    447  |        }
    448  |
    449  |        /* Check that this person/role object is not referenced by name (legacy stuff) */
    450  |        /* But allow overriding this check in NRTM mode and with override_integrity    */
    451  |        if(IS_DUMMY_ALLOWED(tr->mode))break;
    452  |
    453  |        for (i=0; t_ipn[i] != NULL; i++) {
    454  |         /* Calculate number of references */
    455  |
    456  |         g_string_sprintf(query, "SELECT COUNT(*) FROM %s, person_role "
    457  |                                 "WHERE person_role.object_id=%s.pe_ro_id "
    458  |                                 "AND person_role.nic_hdl='%s' ", t_ipn[i], t_ipn[i], tr->save);
    459  |
    460  |         sql_str= get_qresult_str(tr->sql_connection, query->str);
    461  |         if(sql_str) {
    462  |          num_rec = atol(sql_str);  free(sql_str);
    463  |          /* If there are references (no self reference is possible in this case) we cannot delete */
    464  |          if(num_rec>0) {
    465  |            g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, t_ipn[i]);
    466  |            tr->succeeded=0; tr->error |= ERROR_U_OBJ;
    467  |          }
    468  |         } else {
    469  |         /* SQL error occured */
    470  |          tr->succeeded=0; tr->error |= ERROR_U_DBS;
    471  |          g_string_sprintfa(tr->error_script,"E[%d][%s]:%s\n", ERROR_U_DBS, t_ipn[i], SQ_error(tr->sql_connection));
    472  |         }
    473  |        }
    474  |
    475  |        break;
    476  |
    477  |     case C_MT:
    478  |
    479  |         /* Check that this mntner object is not referenced */
    480  |
    481  |        for (i=0; t_imt[i] != NULL; i++) {
    482  |        /* Calculate number of references */
    483  |         sql_str= get_field_str(tr->sql_connection, "COUNT(*)", t_imt[i], "mnt_id", sobject_id, NULL);
    484  |         if(sql_str) {
    485  |          num_rec = atol(sql_str);  free(sql_str);
    486  |          ref_id=tr->object_id;
    487  |          /* Check if it is a self reference  */
    488  |          if(num_rec==1) {
    489  |             sql_str= get_field_str(tr->sql_connection, "object_id", t_imt[i], "mnt_id", sobject_id, NULL);
    490  |             if(sql_str) {
    491  |               ref_id = atol(sql_str);  free(sql_str);
    492  |             } else {
    493  |               tr->succeeded=0; tr->error |= ERROR_U_DBS; break;
    494  |             }
    495  |          }
    496  |          /* If there are references (and not the only self reference) we cannot delete */
    497  |          if((num_rec>1) || (ref_id!=tr->object_id)) {
    498  |            g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, t_imt[i]);
    499  |            tr->succeeded=0; tr->error |= ERROR_U_OBJ;
    500  |          }
    501  |         } else {
    502  |          tr->succeeded=0; tr->error |= ERROR_U_DBS;
    503  |         }
    504  |        }
    505  |        break;
    506  |
    507  |     case C_RS:
    508  |     case C_AS:
    509  |         /* Check that this set object is not referenced */
    510  |         /* Calculate number of references */
    511  |         sql_str= get_field_str(tr->sql_connection, "COUNT(*)", "member_of", "set_id", sobject_id, NULL);
    512  |         if(sql_str) {
    513  |          num_rec = atol(sql_str);  free(sql_str);
    514  |          /* XXX though set may contain other sets as memebers, */
    515  |          /* there is no member-of attribute in these objects. */
    516  |          /* So no self-reference is possible */
    517  |          if(num_rec!=0) {
    518  |            g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, "member_of");
    519  |            /*tr->succeeded=0; tr->error |= ERROR_U_OBJ;*/
    520  | 	  /* XXX Do not refuse the transaction but change the object to dummy */
    521  | 	  /* Update the history table */
    522  |                g_string_sprintf(query,  "INSERT history "
    523  | 	                                "SELECT 0, object_id, sequence_id, timestamp, object_type, object, pkey, serial, prev_serial "
    524  |                                         "FROM last "
    525  |                                         "WHERE object_id=%ld ", tr->object_id);
    526  |
    527  |
    528  |                sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    529  |                if (sql_err) {
    530  | 		ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    531  |                 tr->succeeded=0;
    532  |                 tr->error |=ERROR_U_DBS;
    533  | 		die;
    534  |                }
    535  |
    536  |                /* insert new version into the last */
    537  |                timestamp=time(NULL);
    538  |
    539  | 	      /* update the main table */
    540  | 	      g_string_sprintf(query, "UPDATE %s SET dummy=1 WHERE object_id=%ld ", DF_get_class_sql_table(tr->class_type), tr->object_id);
    541  |
    542  |               sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    543  |               if (sql_err) {
    544  | 	       ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    545  |                tr->succeeded=0;
    546  |                tr->error |= ERROR_U_DBS;
    547  | 	       die;
    548  |               }
    549  |
    550  |               /* empty the contents, but leave in the table to prevent re-use of object_id */
    551  |               g_string_sprintf(query, "UPDATE last SET object='DUMMY SET', object_type=%d, sequence_id=%ld, timestamp=%ld  WHERE object_id=%ld ", DUMMY_TYPE, tr->sequence_id+1, timestamp, tr->object_id);
    552  |
    553  |               sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    554  |               if (sql_err) {
    555  | 	       ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    556  |                tr->succeeded=0;
    557  |                tr->error |= ERROR_U_DBS;
    558  | 	       die;
    559  |               }
    560  |               return(0);
    561  |
    562  |          }
    563  |         } else {
    564  |          tr->succeeded=0; tr->error |= ERROR_U_DBS;
    565  |         }
    566  |         break;
    567  |
    568  |     default:
    569  |         break;
    570  |    }
    571  |
    572  |  g_string_free(query, TRUE);
    573  |
    574  |  /* Check if we have passed referential integrity check */
    575  |  if(tr->succeeded) return(0); else return(-1);
    576  |
    577  | }
    578  |
    579  | /************************************************************
    580  | * int UD_delete()                                              *
    581  | *                                                           *
    582  | * Deletes the object                                        *
    583  | *                                                           *
    584  | * It deletes the object from all relevant tables.
    585  | * Then it updates the radix tree for routes, inetnums
    586  | * and rev.domains           *
    587  | *                                                           *
    588  | ************************************************************/
    589  | int UD_delete(Transaction_t *tr)
    590  | {
    591  | GString *query;
    592  | int err=0;
    593  | int i;
    594  | long timestamp;
    595  | int sql_err;
    596  |
    597  |  /* Try to allocate g_string. Return on error */
    598  |  if ((query = g_string_sized_new(STR_XXL)) == NULL){
    599  |    ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
    600  |    tr->succeeded=0;
    601  |    tr->error|=ERROR_U_MEM;
    602  |    die;
    603  |  }
    604  |
    605  |
    606  | /* Lock all relevant tables */
    607  |    g_string_sprintf(query, "LOCK TABLES ");
    608  |
    609  |    /* we need to lock tables for mntner and role differently, otherwise there will be duplicates (names names, etc.)*/
    610  |    if((tr->class_type!=C_MT) && (tr->class_type!=C_RO)){
    611  |     g_string_sprintfa(query, " %s WRITE,",  DF_get_class_sql_table(tr->class_type));
    612  |
    613  |     for (i=0; tables[tr->class_type][i] != NULL; i++)
    614  |       g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
    615  |    } else { /* mntner and role are special cases */
    616  |       g_string_sprintfa(query, " mntner WRITE, person_role WRITE, ");
    617  |    }
    618  |
    619  |     for (i=TAB_START; tables[tr->class_type][i] != NULL; i++)
    620  |       g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
    621  |
    622  |     g_string_sprintfa(query, " last WRITE, history WRITE ");
    623  |
    624  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    625  |     if (sql_err) {
    626  | 	 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    627  |          tr->succeeded=0;
    628  |          tr->error |=ERROR_U_DBS;
    629  | 	 die;
    630  |     }
    631  | /* Update the history table */
    632  | /* XXX Crash recovery: */
    633  | /* If history was not updated - we will create a record */
    634  | /* If history was already updated but last wasn't - we will just replace the record */
    635  | /* If history and last were already updated - we will have an empty query - 0 rows should be affected */
    636  |     g_string_sprintf(query,     "REPLACE history "
    637  | 				"SELECT 0, object_id, sequence_id, timestamp, object_type, object, pkey, serial, prev_serial "
    638  |        				"FROM last "
    639  |        				"WHERE object_id=%ld AND sequence_id=%ld ", tr->object_id, tr->sequence_id);
    640  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    641  |     if (sql_err) {
    642  | 	 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    643  |          tr->succeeded=0;
    644  |          tr->error |=ERROR_U_DBS;
    645  | 	 die;
    646  |     }
    647  |
    648  | /* Delete records from the leaf and aux tables */
    649  |     for (i=TAB_START; tables[tr->class_type][i] != NULL; i++) {
    650  |      g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld ", tables[tr->class_type][i], tr->object_id);
    651  |      sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    652  |     /*    ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s query (delete): %s\n", UD_TAG, query->str);*/
    653  |        if (sql_err) {
    654  | 	 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    655  |          tr->succeeded=0;
    656  |          tr->error |=ERROR_U_DBS;
    657  | 	 die;
    658  |        }
    659  |     }
    660  |
    661  |
    662  |
    663  | /* Process the MAIN table  */
    664  |     g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld ", DF_get_class_sql_table(tr->class_type), tr->object_id);
    665  |
    666  |
    667  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    668  |     if (sql_err) {
    669  | 	 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    670  |          tr->succeeded=0;
    671  |          tr->error |=ERROR_U_DBS;
    672  | 	 die;
    673  |     }
    674  |
    675  |
    676  |   /* insert new version into the last */
    677  |   timestamp=time(NULL);
    678  |
    679  |  /* empty the contents, but leave in the table to restrict re-use of object_id */
    680  |  /* XXX change sequence_id=0 so it is easy to say that the object was deleted */
    681  |   g_string_sprintf(query, "UPDATE last SET object='', timestamp=%ld, sequence_id=0  WHERE object_id=%ld ", timestamp, tr->object_id);
    682  |
    683  |   sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    684  |   if (sql_err) {
    685  | 	 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    686  |          tr->succeeded=0;
    687  |          tr->error |= ERROR_U_DBS;
    688  | 	 die;
    689  |   }
    690  |
    691  |
    692  |
    693  |  /* Unlock all tables */
    694  |   g_string_sprintf(query, "UNLOCK TABLES ");
    695  |   sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    696  |   if (sql_err) {
    697  | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    698  |         tr->succeeded=0;
    699  |         tr->error |= ERROR_U_DBS;
    700  | 	die;
    701  |   }
    702  |
    703  |
    704  |   g_string_free(query, TRUE);
    705  |
    706  |   return(err);
    707  |
    708  | } /* delete() */
    709  |
    710  |
    711  |
    712  |      	       /* Do more in the forest
    713  |    * Update radix tree for route and inetnum
    714  |    */
    715  |
    716  | int UD_update_rx(Transaction_t *tr, rx_oper_mt mode)
    717  | {
    718  | rp_upd_pack_t *packptr = tr->packptr;
    719  | int err=0;
    720  |
    721  |
    722  |   if(!IS_STANDALONE(tr->mode)) { /* only if server */
    723  |
    724  |
    725  |     /* Only for these types of objects and only if we have collected data (tr->save != NULL) */
    726  |     if( (   (tr->class_type==C_RT)
    727  | 	 || (tr->class_type==C_IN)
    728  | 	 || (tr->class_type==C_I6)
    729  | 	 || (tr->class_type==C_DN))) {
    730  |       /* Collect some data for radix tree and NH repository update for deletes*/
    731  |       if(mode == RX_OPER_DEL)g_slist_foreach((tr->object)->attributes, get_rx_data, tr);
    732  |
    733  |       /* Except for regular domains we need to update radix tree */
    734  |       if(ACT_UPD_RX(tr->action)){
    735  |        packptr->key = tr->object_id;
    736  |        if( RP_pack_node(mode, packptr, tr->source_hdl) == RX_OK ) {
    737  | 	err = 0;
    738  |        } else {
    739  | 	err = (-1);
    740  | 	ER_perror(FAC_UD, UD_BUG, "cannot update radix tree\n");
    741  | 	die;
    742  |        }
    743  |       } /* update radix tree */
    744  |     }
    745  |   }
    746  |   return(err);
    747  | }
    748  |
    749  |
    750  |