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 |