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 | /************************************************************ 37 | * int UD_lock/unlock_serial() * 38 | * * 39 | * Performs lockind/unlocking of the relevant tables * 40 | * * 41 | * Returns: * 42 | * 0 - success * 43 | * Non-zero if error occured (XXX dies now) * 44 | * * 45 | ************************************************************/ 46 | int UD_lock_serial(Transaction_t *tr) 47 | { 48 | int sql_err; 49 | 50 | /* lock all tables we are going to update and commit */ 51 | /* this also includes transaction_rec table, as we update the status */ 52 | sql_err=SQ_execute_query(tr->sql_connection, "LOCK TABLES serials WRITE, failed_transaction WRITE, transaction_rec WRITE ", NULL); 53 | if (sql_err) { 54 | ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), "LOCK TABLES serials WRITE, failed_transaction WRITE, transaction_rec WRITE "); 55 | die; 56 | } 57 | return(sql_err); 58 | } 59 | 60 | int UD_unlock_serial(Transaction_t *tr) 61 | { 62 | int sql_err; 63 | 64 | sql_err=SQ_execute_query(tr->sql_connection, "UNLOCK TABLES ", NULL); 65 | if (sql_err) { 66 | ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), "UNLOCK TABLES"); 67 | die; 68 | } 69 | return(sql_err); 70 | } 71 | 72 | 73 | /************************************************************ 74 | * UD_create_serial() * 75 | * * 76 | * Creates a serial record for given transaction * 77 | * For updates creates 2 serial records (DEL+ADD) * 78 | * * 79 | * Important fields of transaction are: * 80 | * tr->action TR_CREATE/TR_UPDATE/TR_DELETE * 81 | * tr->object_id should be filled in * 82 | * tr->sequence_id should be set to object updated * 83 | * * 84 | * So given object with id=k and seq=n * 85 | * Create: ADD(k,n) * 86 | * Update: ~S(k,n), ADD(k,n+1) * 87 | * Delete: ~S(k,n), DEL(k,n) * 88 | * * 89 | * Returns: * 90 | * currnt serial number. * 91 | * -1 in case of an error * 92 | * * 93 | *************************************************************/ 94 | 95 | long UD_create_serial(Transaction_t *tr) 96 | { 97 | GString *query; 98 | long current_serial=0; 99 | int sql_err; 100 | int operation; 101 | long timestamp; 102 | long sequence_id; 103 | 104 | if ((query = g_string_sized_new(STR_XL)) == NULL){ 105 | tr->succeeded=0; 106 | tr->error |= ERROR_U_MEM; 107 | ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n"); 108 | die; 109 | } 110 | 111 | /* Calculate the object_id - should be max+1 */ 112 | // tr->serial_id = get_minmax_id(tr->sql_connection, "serial_id", "serials", 1) +1; 113 | // TR_update_id(tr); 114 | 115 | /* fprintf(stderr, "creating serial\n"); */ 116 | /* if the transaction failed store it in transaction table */ 117 | if(tr->succeeded==0){ 118 | if(ACT_DELETE(tr->action))operation=OP_DEL; else operation=OP_ADD; 119 | 120 | g_string_sprintf(query, "INSERT serials SET " 121 | "thread_id=%d, object_id=%ld, sequence_id=0, " 122 | "atlast=2, " 123 | "operation=%d ", tr->thread_ins, tr->object_id, operation); 124 | 125 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL); 126 | 127 | if (sql_err) { 128 | ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str); 129 | die; 130 | current_serial=-1; 131 | } 132 | else { 133 | current_serial=mysql_insert_id(tr->sql_connection); 134 | timestamp=time(NULL); 135 | // if(tr->serial_id!=current_serial) die; /* may be the implementation changed */ 136 | g_string_sprintf(query, "INSERT failed_transaction SET " 137 | "thread_id=%d, serial_id=%ld, timestamp=%ld, " 138 | "object='%s' ", tr->thread_ins, current_serial, timestamp, tr->object->object->str); 139 | /* make a record in transaction table */ 140 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL); 141 | if (sql_err) { 142 | ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str); 143 | die; 144 | current_serial=-1; 145 | } 146 | } 147 | g_string_free(query, TRUE); 148 | return(current_serial); 149 | } 150 | 151 | 152 | /* if the transaction has succeeded */ 153 | sequence_id=tr->sequence_id; 154 | /* If this is an update or delete */ 155 | if(!ACT_CREATE(tr->action)) { 156 | /* Increase the sequence_id so we insert correct ADD serial in case of Update */ 157 | sequence_id=tr->sequence_id + 1; 158 | /* set the atlast field of the latest record for this object to 0 */ 159 | /* because it is moved to history */ 160 | g_string_sprintf(query, "UPDATE serials SET atlast=0, thread_id=%d " 161 | "WHERE object_id=%ld " 162 | "AND sequence_id=%ld ", tr->thread_upd, tr->object_id, sequence_id-1); 163 | 164 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL); 165 | if (sql_err) { // we can have empty updates, but not errors 166 | ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str); 167 | die; 168 | current_serial=-1; 169 | } 170 | } 171 | /* XXX below is a code for protocol v2, when updates are atomic */ 172 | /* XXX this is fine (and should always be used) for NRTM, since we */ 173 | /* XXX store failed transactions and playback stream exactly as it comes */ 174 | /* XXX However, for update this may be configurable option */ 175 | /* XXX In case v1 protocol both sections (DEL + ADD) should be executed */ 176 | /* if this a DEL */ 177 | if(ACT_DELETE(tr->action)) { 178 | /* generate DEL serial */ 179 | g_string_sprintf(query, "INSERT serials SET " 180 | "thread_id=%d, object_id=%ld, " 181 | "sequence_id=%ld, " 182 | "atlast=0, " 183 | "operation=%d ", tr->thread_ins, tr->object_id, sequence_id-1, OP_DEL); 184 | 185 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL); 186 | if (sql_err) { 187 | ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str); 188 | die; 189 | current_serial=-1; 190 | } 191 | 192 | if(current_serial!=-1)current_serial=mysql_insert_id(tr->sql_connection); 193 | 194 | } 195 | else { /* otherwise this is an ADD */ 196 | 197 | /* now insert creation serial */ 198 | g_string_sprintf(query, "INSERT serials SET " 199 | "thread_id=%d, object_id=%ld, " 200 | "sequence_id=%ld, " 201 | "atlast=1, " 202 | "operation=%d ", tr->thread_ins, tr->object_id, sequence_id, OP_ADD); 203 | 204 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL); 205 | if (sql_err) { 206 | ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str); 207 | die; 208 | current_serial=-1; 209 | } 210 | 211 | if(current_serial!=-1){ 212 | current_serial=mysql_insert_id(tr->sql_connection); 213 | // if(tr->serial_id!=current_serial) die; /* may be the implementation changed */ 214 | } 215 | 216 | } 217 | g_string_free(query, TRUE); 218 | return(current_serial); 219 | } 220 | /************************************************************ 221 | * UD_comrol_serial() * 222 | * * 223 | * Commits/Rollbacks a serial record for given transaction * 224 | * Returns: * 225 | * 0 in success * 226 | * -1 in case of an error * 227 | * * 228 | *************************************************************/ 229 | 230 | char *Q_rollback_serial1="DELETE FROM serials WHERE thread_id=%ld "; 231 | char *Q_rollback_serial2="UPDATE serials SET atlast=1, thread_id=0 WHERE thread_id=%ld "; 232 | char *Q_rollback_transaction="DELETE FROM failed_transaction WHERE thread_id=%ld "; 233 | char *Q_commit_serial="UPDATE serials SET thread_id=0 WHERE thread_id=%ld OR thread_id=%ld "; 234 | char *Q_commit_transaction="UPDATE failed_transaction SET thread_id=0 WHERE thread_id=%ld "; 235 | 236 | 237 | 238 | int UD_comrol_serial(Transaction_t *tr, int commit) 239 | { 240 | GString *query; 241 | int sql_err; 242 | char *Q_transaction; 243 | 244 | /* check if something is left in serials from the crash */ 245 | 246 | if ((query = g_string_sized_new(STR_XL)) == NULL){ 247 | ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n"); 248 | tr->succeeded=0; 249 | tr->error |= ERROR_U_MEM; 250 | return(ERROR_U_MEM); 251 | } 252 | 253 | /* compose the appropriate query depending on operation (commit/rollback) */ 254 | if(commit) { 255 | /* commit changes to serials table */ 256 | g_string_sprintf(query, Q_commit_serial, tr->thread_ins, tr->thread_upd); 257 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL); 258 | if (sql_err) { 259 | ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str); 260 | die; 261 | } 262 | Q_transaction=Q_commit_transaction; 263 | } else { 264 | /* delete new insertions */ 265 | g_string_sprintf(query, Q_rollback_serial1, tr->thread_ins); 266 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL); 267 | if (sql_err) { 268 | ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str); 269 | die; 270 | } 271 | /* restore modified atlast */ 272 | g_string_sprintf(query, Q_rollback_serial2, tr->thread_upd); 273 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL); 274 | if (sql_err) { 275 | ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str); 276 | die; 277 | } 278 | Q_transaction=Q_rollback_transaction; 279 | } 280 | 281 | /* clean up transaction table */ 282 | g_string_sprintf(query, Q_transaction, tr->thread_ins); 283 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL); 284 | if (sql_err) { 285 | ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str); 286 | die; 287 | } 288 | g_string_free(query, TRUE); 289 | return(0); 290 | } 291 | 292 |