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 |