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 |