Source File modules/ud/ud_serial.c

    1    | /***************************************
    2    |
    3    |   Functions for handling serials
    4    |
    5    |   Status: NOT REVUED, NOT TESTED
    6    |
    7    |  Author(s):       Andrei Robachevsky
    8    |
    9    |   ******************/ /******************
    10   |   Modification History:
    11   |         andrei (08/02/2000) Created.
    12   |   ******************/ /******************
    13   |   Copyright (c) 2000                              RIPE NCC
    14   |
    15   |   All Rights Reserved
    16   |
    17   |   Permission to use, copy, modify, and distribute this software and its
    18   |   documentation for any purpose and without fee is hereby granted,
    19   |   provided that the above copyright notice appear in all copies and that
    20   |   both that copyright notice and this permission notice appear in
    21   |   supporting documentation, and that the name of the author not be
    22   |   used in advertising or publicity pertaining to distribution of the
    23   |   software without specific, written prior permission.
    24   |
    25   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
    26   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
    27   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
    28   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
    29   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    30   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    31   |  ***************************************/
    32   | #include "ud.h"
    33   | #include "ud_int.h"
    34   | #include "ud_tr.h"
    35   |
    36   | void BEKILLED()
    37   | {
    38   |
    39   |  kill(getpid(), SIGKILL);
    40   |
    41   |
    42   | }
    43   |
    44   | /************************************************************
    45   | * int UD_lock/unlock_serial()                               *
    46   | *                                                           *
    47   | * Performs lockind/unlocking of the relevant tables         *
    48   | *                                                           *
    49   | * Returns:                                                  *
    50   | * 0 - success                                               *
    51   | * Non-zero if error occured (XXX dies now)                  *
    52   | *                                                           *
    53   | ************************************************************/
    54   | int UD_lock_serial(Transaction_t *tr)
    55   | {
    56   | int sql_err;
    57   |
    58   | /* lock all tables we are going to update and commit */
    59   | /* this also includes transaction_rec table, as we update the status */
    60   |    sql_err=SQ_execute_query(tr->sql_connection, "LOCK TABLES serials WRITE, failed_transaction WRITE, transaction_rec WRITE ", NULL);
    61   |    if (sql_err) {
    62   | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), "LOCK TABLES serials WRITE, failed_transaction WRITE, transaction_rec WRITE ");
    63   |         die;
    64   |     }
    65   |  return(sql_err);
    66   | }
    67   |
    68   | int UD_unlock_serial(Transaction_t *tr)
    69   | {
    70   | int sql_err;
    71   |
    72   |    sql_err=SQ_execute_query(tr->sql_connection, "UNLOCK TABLES ", NULL);
    73   |    if (sql_err) {
    74   | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), "UNLOCK TABLES");
    75   |         die;
    76   |     }
    77   |  return(sql_err);
    78   | }
    79   |
    80   |
    81   | /************************************************************
    82   | * UD_create_serial()                                        *
    83   | *                                                           *
    84   | * Creates a serial record for given transaction             *
    85   | *                                                           *
    86   | * Important fields of transaction are:                      *
    87   | * tr->action        TR_CREATE/TR_UPDATE/TR_DELETE           *
    88   | * tr->object_id     should be filled in                     *
    89   | * tr->sequence_id   should be set to object updated         *
    90   | *                                                           *
    91   | * So given object with id=k and seq=n                       *
    92   | * Create:  ADD(k,n)                                         *
    93   | * Update:  ~S(k,n), ADD(k,n+1)                              *
    94   | * Delete:  ~S(k,n), DEL(k,n)                                *
    95   | *                                                           *
    96   | * Returns:                                                  *
    97   | *  current serial number.                                    *
    98   | *  -1 in case of an error                                   *
    99   | *                                                           *
    100  | *************************************************************/
    101  |
    102  | long UD_create_serial(Transaction_t *tr)
    103  | {
    104  | GString *query;
    105  | //long current_serial=0;
    106  | int sql_err;
    107  | int operation;
    108  | long timestamp;
    109  | long sequence_id;
    110  |
    111  |    if ((query = g_string_sized_new(STR_XL)) == NULL){
    112  |       tr->succeeded=0;
    113  |       tr->error |= ERROR_U_MEM;
    114  |       ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
    115  |       die;
    116  |    }
    117  |
    118  |  /* Calculate the object_id - should be max+1 */
    119  |  /* XXX we cannot use autoincrement with MyISAM tables */
    120  |  /* XXX because they keep the max inserted id even if  */
    121  |  /* XXX it was deleted later, thus causing gaps we don't want */
    122  |     tr->serial_id = SQ_get_max_id(tr->sql_connection, "serial_id", "serials") +1;
    123  |
    124  |   /* if the transaction failed store it in transaction table */
    125  |   if(tr->succeeded==0){
    126  |     if(ACT_DELETE(tr->action))operation=OP_DEL; else operation=OP_ADD;
    127  |
    128  |     g_string_sprintf(query, "INSERT serials SET "
    129  |                    "thread_id=%d, serial_id=%ld, object_id=%ld, sequence_id=0, "
    130  | 		   "atlast=2, "
    131  | 		   "operation=%d ", tr->thread_ins, tr->serial_id, tr->object_id, operation);
    132  |
    133  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    134  |
    135  |     if (sql_err) {
    136  | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    137  | //	current_serial=-1;
    138  | 	die;
    139  |     }
    140  |     else {
    141  |        timestamp=time(NULL);
    142  |        g_string_sprintf(query, "INSERT failed_transaction SET "
    143  |                    "thread_id=%d, serial_id=%ld, timestamp=%ld, "
    144  | 		   "object='%s' ", tr->thread_ins, tr->serial_id, timestamp, tr->object->object->str);
    145  |        /* make a record in transaction table */
    146  |        sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    147  |        if (sql_err) {
    148  | 	 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    149  | 	 die;
    150  |        }
    151  |     }
    152  |     g_string_free(query, TRUE);
    153  |     return(tr->serial_id);
    154  |   }
    155  |
    156  |
    157  |   /* if the transaction has succeeded */
    158  |   sequence_id=tr->sequence_id;
    159  |   /* If this is an update or delete */
    160  |   if(!ACT_CREATE(tr->action)) {
    161  |     /* Increase the sequence_id so we insert correct ADD serial in case of Update */
    162  |     sequence_id=tr->sequence_id + 1;
    163  |
    164  |     /* set the atlast field of the latest record for this object to 0 */
    165  |     /* because it is moved to history */
    166  |     g_string_sprintf(query, "UPDATE serials SET atlast=0, thread_id=%d "
    167  |                    "WHERE object_id=%ld "
    168  |                    "AND sequence_id=%ld ", tr->thread_upd, tr->object_id, sequence_id-1);
    169  |
    170  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    171  |     if (sql_err) { // we can have empty updates, but not errors
    172  | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    173  | 	die;
    174  |     }
    175  |   }
    176  |   /* XXX below is a code for protocol v2, where updates are atomic */
    177  |   /* XXX this is fine (and should always be used) for NRTM, since we */
    178  |   /* XXX store failed transactions and playback stream exactly as it comes */
    179  |   /* XXX However, for update this may be configurable option */
    180  |   /* XXX In case v1 protocol both sections (DEL + ADD) should be executed */
    181  |
    182  |   /* get the next serial_id */
    183  |   /* XXX we cannot use autoincrement with MyISAM tables */
    184  |   /* XXX because they keep the max inserted id even if  */
    185  |   /* XXX it was deleted later, thus causing gaps we don't want */
    186  |   tr->serial_id = SQ_get_max_id(tr->sql_connection, "serial_id", "serials") +1;
    187  |
    188  |   /* if this a DEL */
    189  |
    190  |
    191  |   if(ACT_DELETE(tr->action)) {
    192  |     /* generate DEL serial */
    193  |
    194  |     g_string_sprintf(query, "INSERT serials SET "
    195  |                    "thread_id=%d, serial_id=%ld, object_id=%ld, "
    196  |                    "sequence_id=%ld, "
    197  |                    "atlast=0, "
    198  |                    "operation=%d ", tr->thread_ins, tr->serial_id, tr->object_id, sequence_id-1, OP_DEL);
    199  |
    200  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    201  |     if (sql_err) {
    202  | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    203  | 	die;
    204  |     }
    205  |
    206  |   }
    207  |   else { /* otherwise this is an ADD */
    208  |
    209  |    /* now insert creation serial */
    210  |    g_string_sprintf(query, "INSERT serials SET "
    211  |                   "thread_id=%d, serial_id=%ld, object_id=%ld, "
    212  |                   "sequence_id=%ld, "
    213  |                   "atlast=1, "
    214  |                   "operation=%d ", tr->thread_ins, tr->serial_id, tr->object_id, sequence_id, OP_ADD);
    215  |
    216  |    sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    217  |    if (sql_err) {
    218  | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    219  | 	die;
    220  |    }
    221  |
    222  |   }
    223  |   g_string_free(query, TRUE);
    224  | return(tr->serial_id);
    225  | }
    226  | /************************************************************
    227  | * UD_comrol_serial()                                        *
    228  | *                                                           *
    229  | * Commits/Rollbacks a serial record for given transaction   *
    230  | * Returns:                                                  *
    231  | * 0 in success                                              *
    232  | *  -1 in case of an error                                   *
    233  | *                                                           *
    234  | *************************************************************/
    235  |
    236  | char *Q_rollback_serial1="DELETE FROM serials WHERE thread_id=%ld ";
    237  | char *Q_rollback_serial2="UPDATE serials SET atlast=1, thread_id=0 WHERE thread_id=%ld ";
    238  | char *Q_rollback_transaction="DELETE FROM failed_transaction WHERE thread_id=%ld ";
    239  | char *Q_commit_serial="UPDATE serials SET thread_id=0 WHERE thread_id=%ld OR thread_id=%ld ";
    240  | char *Q_commit_transaction="UPDATE failed_transaction SET thread_id=0 WHERE thread_id=%ld ";
    241  |
    242  |
    243  |
    244  | int UD_comrol_serial(Transaction_t *tr, int commit)
    245  | {
    246  | GString *query;
    247  | int sql_err;
    248  | char *Q_transaction;
    249  |
    250  |    /* check if something is left in serials from the crash */
    251  |
    252  |    if ((query = g_string_sized_new(STR_XL)) == NULL){
    253  |       ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
    254  |       tr->succeeded=0;
    255  |       tr->error |= ERROR_U_MEM;
    256  |       return(ERROR_U_MEM);
    257  |    }
    258  |
    259  |    /* compose the appropriate query depending on operation (commit/rollback) */
    260  |    if(commit) {
    261  | 	   /* commit changes to serials table */
    262  | 	   g_string_sprintf(query, Q_commit_serial, tr->thread_ins, tr->thread_upd);
    263  | 	   sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    264  |            if (sql_err) {
    265  | 	     ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    266  |              die;
    267  |            }
    268  | 	   Q_transaction=Q_commit_transaction;
    269  |    } else {
    270  | 	   /* delete new insertions */
    271  | 	   g_string_sprintf(query, Q_rollback_serial1, tr->thread_ins);
    272  | 	   sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    273  |            if (sql_err) {
    274  | 	     ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    275  |              die;
    276  |            }
    277  | 	   /* restore modified atlast */
    278  |            g_string_sprintf(query, Q_rollback_serial2, tr->thread_upd);
    279  | 	   sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    280  |            if (sql_err) {
    281  | 	     ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    282  |              die;
    283  |            }
    284  | 	   Q_transaction=Q_rollback_transaction;
    285  |    }
    286  |
    287  |     /* clean up transaction  table */
    288  |     g_string_sprintf(query, Q_transaction, tr->thread_ins);
    289  |     sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
    290  |     if (sql_err) {
    291  | 	ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    292  |         die;
    293  |     }
    294  |  g_string_free(query, TRUE);
    295  |  return(0);
    296  | }
    297  |
    298  |