utils/history/archive.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following functions.
  1. process_arguments
  2. archive_table
  3. patch_serial
  4. patch_pseudo_delete
  5. patch_delete_object
  6. patch_serials
  7. compute_begin_time
  8. archive2
  9. delete_from
  10. update
  11. insert_select
  12. count
  13. 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 }

/* [<][>][^][v][top][bottom][index][help] */