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=mysql_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=mysql_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=mysql_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 |