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