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 |