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