## 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] */