/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following functions.
- process_arguments
- archive_table
- patch_serial
- patch_pseudo_delete
- patch_delete_object
- patch_serials
- compute_begin_time
- archive2
- delete_from
- update
- insert_select
- count
- main
1 /***************************************
2 $Revision: 1.1 $
3
4 Archive. archive.c - whois DB archiving.
5
6 Status: REVIEWED, TESTED, COMPLETE
7
8 Implementation by: Tiago Antao
9
10 ******************/ /******************
11 Copyright (c) 2002 RIPE NCC
12
13 All Rights Reserved
14
15 Permission to use, copy, modify, and distribute this software and its
16 documentation for any purpose and without fee is hereby granted,
17 provided that the above copyright notice appear in all copies and that
18 both that copyright notice and this permission notice appear in
19 supporting documentation, and that the name of the author not be
20 used in advertising or publicity pertaining to distribution of the
21 software without specific, written prior permission.
22
23 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
25 AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
26 DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
27 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
28 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
29 ***************************************/
30
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <time.h>
35 #include <rip.h>
36 #include "miniconf.h"
37 #include "dbsupport.h"
38 #include "aconf.h"
39
40 long begin_time = 0;
41 long end_time;
42
43 /*
44 process_arguments: processes command-line arguments.
45 */
46 void process_arguments(int argv, char** argc) {
/* [<][>][^][v][top][bottom][index][help] */
47 if (argv != 3 && argv != 1) {
48 printf("Usage: %s [-begin <ts>]\n", argc[0]);
49 exit(1);
50 }
51
52 if (argv == 3) {
53 if (strcmp(argc[1], "-begin") == 0) {
54 begin_time = atol(argc[2]); //assume is number
55 }
56 else {
57 printf("Usage: %s [-begin <ts>]\n", argc[0]);
58 exit(1);
59 }
60 }
61 }
62
63 //thread_id!!!
64 //check errors
65 /*
66 archive_table: archives a table
67
68 Archives a table (last or history).
69
70 For each record (observing timestamps) on to archive table
71 Reads record, writes record to archive2.
72 */
73 void archive_table(char* table) {
/* [<][>][^][v][top][bottom][index][help] */
74 SQ_result_set_t* rs;
75 SQ_row_t* row;
76 char select_query[300];
77 char* insert_query;
78 char* object_buffer;
79 char pkey_buffer[300];
80 int object_size;
81 char object_id[50];
82 char sequence_id[50];
83 char operation[50];
84 long cont;
85
86 cont = 0;
87
88 sprintf(select_query,
89 "SELECT object_id, sequence_id, pkey,"
90 " timestamp, object_type, object"
91 " FROM %s "
92 " WHERE timestamp>=%ld AND timestamp<%ld AND pkey<>''",
93 table, begin_time, end_time);
94 //printf("%s\n", select_query);
95 SQ_execute_query_nostore(RIPE_conn, select_query, &rs);
96
97
98 while ((row = SQ_row_next(rs)) != NULL) {
99 object_size = strlen(SQ_get_column_string_nocopy(rs,row,5));
100 object_buffer = UT_malloc(object_size*2 + 1); //worst case
101 insert_query = UT_malloc(object_size*2 + 300);
102
103 prepare_string_attribute(SQ_get_column_string_nocopy(rs,row,5),
104 object_buffer);
105 prepare_string_attribute(SQ_get_column_string_nocopy(rs,row,2),
106 pkey_buffer);
107 check_null(rs, row, 0, object_id);
108 check_null(rs, row, 1, sequence_id);
109 //check_null(rs, row, 3, operation);
110 cont ++;
111 sprintf(insert_query,
112 "INSERT INTO archive2(object_id, pkey, object_type, operation, "
113 "timestamp, sequence_id, object) "
114 "VALUES (%s,'%s', %s, 1, %s, %s,'%s')\0",
115 object_id,
116 pkey_buffer,
117 SQ_get_column_string_nocopy(rs,row,4),
118 //operation,
119 SQ_get_column_string_nocopy(rs,row,3),
120 sequence_id,
121 object_buffer
122 );
123 if (SQ_execute_query(archive_conn, insert_query, NULL) != 0) {
124 printf("'%s' Failed!\n", insert_query);
125 exit(1);
126 }
127
128 UT_free(object_buffer);
129 UT_free(insert_query);
130 }
131 SQ_free_result(rs);
132
133 //printf ("Archived %ld\n", cont);
134
135 }
136
137
138 /*
139 patch_serial: patches serials into archive2
140 */
141 void patch_serial(long object_id, long sequence, long serial) {
/* [<][>][^][v][top][bottom][index][help] */
142 char update_query[100];
143
144 sprintf(update_query,
145 "UPDATE archive2 "
146 " SET serial_id = %ld "
147 " WHERE object_id = %ld AND sequence_id = %ld ",
148 serial, object_id, sequence);
149
150 //printf("%s\n", update_query);
151 SQ_execute_query(archive_conn, update_query, NULL);
152 }
153
154 /*
155 patch_pseudo_delete: patches pseudo delete operation
156 */
157 void patch_pseudo_delete(long object_id, long sequence) {
/* [<][>][^][v][top][bottom][index][help] */
158 char update_query[100];
159
160 sprintf(update_query,
161 "UPDATE archive2 "
162 " SET operation = 3 "
163 " WHERE object_id = %ld AND sequence_id = %ld ",
164 object_id, sequence);
165
166 //printf("%s\n", update_query);
167 SQ_execute_query(archive_conn, update_query, NULL);
168 }
169
170 /*
171 */
172 void patch_delete_object(long object_id, long sequence) {
/* [<][>][^][v][top][bottom][index][help] */
173 SQ_result_set_t* rs;
174 SQ_row_t* row;
175 SQ_row_t* next;
176 long serial;
177 char* object;
178 char* clean_object;
179 char select_query[200];
180 char* update_query;
181
182
183 if (sequence == 0) {
184 return;
185 }
186 sprintf(select_query,
187 "SELECT object FROM history WHERE object_id=%ld AND sequence_id=%ld",
188 object_id, sequence);
189
190 //printf("%s\n", select_query);
191 SQ_execute_query(RIPE_conn, select_query, &rs);
192
193 row = SQ_row_next(rs);
194 if (row == NULL) {
195 printf("Could not patch delete (SELECT) for oid=%ld seq=%ld\n",
196 object_id, sequence);
197 exit(1);
198 }
199
200 object = SQ_get_column_string_nocopy(rs, row, 0);
201 update_query = UT_malloc(strlen(object)*2 + 300);
202 clean_object = UT_malloc(strlen(object)*2 + 5); // this is lame
203 prepare_string_attribute(object, clean_object);
204 sprintf(update_query,
205 "UPDATE archive2 "
206 " SET object='%s', operation=2 "
207 " WHERE object_id=%d AND sequence_id=0",
208 clean_object, object_id);
209 UT_free(clean_object);
210 //printf("%s\n", update_query);
211 if (SQ_execute_query(archive_conn, update_query, NULL) != 0) {
212 printf("Could not patch delete (UPDATE) for oid=%ld seq=%ld\n",
213 object_id, sequence);
214 exit(1);
215 }
216 UT_free(update_query);
217 SQ_free_result(rs);
218 }
219
220 /*
221 patch_serials: patches serials into archive
222
223 SELECT serial_id, operation, object_id, sequence_id
224 FROM serials
225 ORDER BY object_id, sequence_id, operation DESC
226 */
227 void patch_serials() {
/* [<][>][^][v][top][bottom][index][help] */
228 SQ_result_set_t* rs;
229 SQ_row_t* row;
230 SQ_row_t* next;
231 long serial;
232 long sequence;
233 long operation;
234 long object_id;
235 long next_serial;
236 long next_object_id;
237
238
239 SQ_execute_query(RIPE_conn,
240 " SELECT serial_id, operation, object_id, "
241 " sequence_id "
242 " FROM serials "
243 "ORDER BY object_id, sequence_id, operation DESC",
244 &rs);
245
246 row = SQ_row_next(rs);
247 while (row != NULL) {
248 SQ_get_column_int(rs, row, 0, &serial);
249 SQ_get_column_int(rs, row, 1, &operation);
250 SQ_get_column_int(rs, row, 2, &object_id);
251 SQ_get_column_int(rs, row, 3, &sequence);
252
253 next = SQ_row_next(rs);
254
255
256 if (object_id != 0) {
257 // Deletes
258 if (operation == 2) {
259 if (next == NULL) {
260 patch_delete_object(object_id, sequence);
261 patch_serial(object_id, 0, serial);
262 }
263 else {
264 SQ_get_column_int(rs, next, 2, &next_object_id);
265 SQ_get_column_int(rs, next, 0, &next_serial);
266 if (next_object_id == object_id &&
267 next_serial == serial + 1) {
268 patch_pseudo_delete(object_id, sequence+1);
269 }
270 else {
271 patch_delete_object(object_id, sequence);
272 patch_serial(object_id, 0, serial);
273 }
274 }
275 }
276 else {
277 patch_serial(object_id, sequence, serial);
278 }
279 }
280
281 row = next;
282 }
283
284 SQ_free_result(rs);
285
286 /* // TBR removed
287 SQ_execute_query(archive_conn,
288 " UPDATE archive2 "
289 " SET operation=2 "
290 " WHERE object='' ",
291 NULL);
292 */
293 }
294
295 void compute_begin_time() {
/* [<][>][^][v][top][bottom][index][help] */
296 SQ_result_set_t* rs;
297 SQ_row_t* row;
298 long max_archived;
299
300 if (begin_time==0) {
301 SQ_execute_query(archive_conn,
302 "SELECT max(timestamp) FROM archive",
303 &rs);
304 row = SQ_row_next(rs);
305
306 if ((row != NULL) &&
307 (SQ_get_column_int(rs, row, 0, &max_archived)==0)) {
308 begin_time = max_archived + 1;
309 }
310 SQ_free_result(rs);
311 }
312 }
313
314 /*
315 archive2: archives
316
317 Self-documenting? No...
318
319 The general philosophy is: Copy verbatim the content of last and
320 history tables (at least the ones that are not on the current archive)
321 and only then patch in the serials.
322 The rational for this strategy is: It should be assured in as much as
323 possible that no records on last and history are lost, that would be
324 very serious, as such the code on that part should be simple.
325 The serial code can be a little bit more complex, it should not fail,
326 but if it has a bug it is less serious than losing last/history.
327 */
328 void archive2() {
/* [<][>][^][v][top][bottom][index][help] */
329 if (SQ_execute_query(RIPE_conn,
330 "LOCK TABLES history READ, serials READ, last READ",
331 NULL) != 0) {
332 printf("Could not lock tables for reading:\n%s\n", SQ_error(RIPE_conn));
333 exit(1);
334 }
335
336 compute_begin_time();
337 //printf("Archiving last\n");
338 archive_table("last");
339 //printf("Archiving history\n");
340 archive_table("history");
341 //printf("Patching serials/deletes\n");
342 patch_serials();
343
344 SQ_execute_query(RIPE_conn, "UNLOCK TABLES", NULL);
345
346 }
347
348 /*
349 delete_from: simple function to make code writing more clean
350
351 Should it be on a separate place?
352 */
353 void delete_from(char *from) {
/* [<][>][^][v][top][bottom][index][help] */
354 char delete[200];
355
356 sprintf(delete, "DELETE FROM %s", from);
357 SQ_execute_query(archive_conn, delete, NULL);
358 }
359
360 /*
361 update: simple function to make code writing more clean
362
363 Should it be on a separate place?
364 */
365 void update(char* table, char* set, char* where) {
/* [<][>][^][v][top][bottom][index][help] */
366 char update[200];
367
368 sprintf(update, "UPDATE %s SET %s", table, set);
369 SQ_execute_query(archive_conn, update, NULL);
370 }
371
372 /*
373 insert_select: simple function to make code writing more clean
374
375 Should it be on a separate place?
376 */
377 void insert_select(char* to, char *from, char* distinct) {
/* [<][>][^][v][top][bottom][index][help] */
378 char insert[200];
379
380 sprintf(insert, "INSERT INTO %s SELECT %s * FROM %s", to, distinct, from);
381 SQ_execute_query(archive_conn, insert, NULL);
382 }
383
384 long count(char *query) {
/* [<][>][^][v][top][bottom][index][help] */
385 SQ_result_set_t* rs;
386 SQ_row_t* row;
387 long result;
388
389 if (SQ_execute_query(archive_conn, query, &rs) != 0) {
390 printf("'%s' failed\n", query);
391 exit(1);
392 }
393
394 row = SQ_row_next(rs);
395
396 if (row == NULL) {
397 printf("count for '%s' failed\n", query);
398 exit(1);
399 }
400 SQ_get_column_int(rs, row, 0, &result);
401
402 SQ_free_result(rs);
403
404 return result;
405 }
406
407 /*
408 main: Archive entry point
409
410 This function is reasonably self-commenting.
411 Also the previous hour is not archived to avoid concurrency problems
412 with updates.
413 Also it is important to archive last _before_ of history because of
414 deletes that might occur during the process (repetitions are ok, they
415 will be removed)
416
417 A first execution over a normal database will be sloooow.
418 */
419 int main (int argv, char** argc) {
/* [<][>][^][v][top][bottom][index][help] */
420 process_arguments(argv, argc);
421 read_configuration();
422 end_time = time(NULL) - 3600;
423 get_db_connections();
424
425
426 delete_from("archive2");
427 printf("Beginning archive\n");
428 archive2();
429 //printf("Inserting archive2 into archive\n");
430 update("archive2", "object_id = NULL", "");
431 //printf("Inserting archive2 into archive\n");
432 insert_select("archive", "archive2", "");
433 //printf("delete from archive2\n");
434 delete_from("archive2");
435
436 //close_dbs();
437
438 printf("Ending sucessful execution of archive\n");
439 return 0;
440 }