modules/qi/query_instructions.c

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

FUNCTIONS

This source file includes following functions.
  1. qi_kill_body
  2. sql_execute_watched
  3. create_name_query
  4. create_asblock_query
  5. add_filter
  6. create_query
  7. fast_output
  8. filter
  9. write_results
  10. write_objects
  11. insert_radix_serials
  12. write_radix_immediate
  13. map_qc2rx
  14. run_referral
  15. qi_collect_domain
  16. add_ref_name
  17. qi_collect_ids
  18. qi_fetch_references
  19. QI_execute
  20. instruction_free
  21. QI_free
  22. valid_query
  23. QI_new
  24. QI_queries_to_string

   1 /***************************************
   2   $Revision: 1.50 $
   3 
   4 
   5   Sql module (sq).  This is a mysql implementation of an sql module.
   6 
   7   Status: NOT REVUED, NOT TESTED
   8 
   9   Note: this code has been heavily coupled to MySQL, and may need to be changed
  10   (to improve performance) if a new RDBMS is used.
  11 
  12   ******************/ /******************
  13   Filename            : query_instructions.c
  14   Author              : ottrey@ripe.net
  15   OSs Tested          : Solaris
  16   Problems            : Moderately linked to MySQL.  Not sure which inverse
  17                         attributes each option has.  Would like to modify this
  18                         after re-designing the objects module.
  19   Comments            : Not sure about the different keytypes.
  20   ******************/ /******************
  21   Copyright (c) 1999                              RIPE NCC
  22  
  23   All Rights Reserved
  24   
  25   Permission to use, copy, modify, and distribute this software and its
  26   documentation for any purpose and without fee is hereby granted,
  27   provided that the above copyright notice appear in all copies and that
  28   both that copyright notice and this permission notice appear in
  29   supporting documentation, and that the name of the author not be
  30   used in advertising or publicity pertaining to distribution of the
  31   software without specific, written prior permission.
  32   
  33   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  34   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
  35   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
  36   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  37   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  38   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  39   ***************************************/
  40 #include <stdio.h>
  41 #include <string.h>
  42 #include <glib.h>
  43 
  44 #include "which_keytypes.h"
  45 #include "query_instructions.h"
  46 #include "mysql_driver.h"
  47 #include "rp.h"
  48 #include "stubs.h"
  49 #include "constants.h"
  50 #include "memwrap.h"
  51 #include "wh_queries.h"
  52 
  53 
  54 
  55 /*+ String sizes +*/
  56 #define STR_S   63
  57 #define STR_M   255
  58 #define STR_L   1023
  59 #define STR_XL  4095
  60 #define STR_XXL 16383
  61 
  62 /* XXX this must be removed from here!!! a .h file must be 
  63    generated from xml */
  64 
  65 #include "defs.h"
  66 
  67 /* body of the query thread.
  68 
  69    takes a ptr to structure with all arguments.
  70    returns an int (result of sq_execute_query) cast to (void*) 
  71 
  72    by marek
  73 */
  74 static
  75 void *qi_kill_body(void *arg)
     /* [<][>][^][v][top][bottom][index][help] */
  76 {
  77   SQ_connection_t *sql_connection = arg;
  78   ER_dbg_va(FAC_QI, ASP_QI_LAST_DET,
  79               "rtc: killing SQL connection %d", (sql_connection)->thread_id);
  80   /* abort the running query */
  81   SQ_abort_query(sql_connection);
  82 
  83   return NULL;
  84 }
  85 
  86 /* 
  87    wrapper around sq_execute_query: starts a query 
  88    in a separate thread and starts the socket watcher to cancel the query 
  89    if the socket is closed.
  90 
  91    the execution of the query or watchdog is not guaranteed at all!
  92 
  93    if the rtc was set before, there will be even no attempt to start
  94    a query or watchdog.
  95 
  96    by marek
  97 */
  98 int sql_execute_watched(sk_conn_st *condat, SQ_connection_t **sql_connection, 
     /* [<][>][^][v][top][bottom][index][help] */
  99                         const char *query, SQ_result_set_t **result_ptr)
 100 {
 101   int retval = 0; /* return value of sq_execute_query */
 102   SQ_connection_t *tempcon;
 103 
 104   /* assert that, if defined, result_ptr is initialised to NULL 
 105      prior to calling this function */
 106   if( result_ptr != NULL ) {
 107     dieif( *result_ptr != NULL );
 108   }
 109 
 110   /* don't even try to perform the query/fire up watchdog
 111      if rtc is already set. Do this only if not set yet. */
 112   if( condat->rtc == 0 ) {
 113     
 114     /* make clean */
 115     SK_watchclear(condat);
 116     
 117     /* set watchdog to execute the abort function */
 118     SK_watchexec(condat, qi_kill_body, *sql_connection);
 119     
 120     /* start the watchdog */
 121     SK_watchstart(condat);
 122     
 123     /* start query. An error may be returned if the query is aborted */
 124     retval = SQ_execute_query(*sql_connection, query, result_ptr);
 125     
 126     /* but short queries will complete before the watchdog kills the
 127        connection */
 128     
 129     SK_watchstop(condat);
 130     
 131 
 132     /* if the watchdog triggered, then it is guaranteed that
 133        the kill_body function was invoked and therefore the sql-connection
 134        is now unusable... 
 135        Close and reopen it for cleanup, use temporary connection
 136        to keep the login details */
 137     if( condat->rtc != 0 ) {
 138       /* can't rely on the error code from mysql!
 139        */ 
 140     
 141       /* one thing: this code must be entered ONLY if the kill_body
 142          thing was invoked by the watchdog. 
 143       */
 144     
 145       /* if result is defined, free it here before destroying the 
 146          associated connection */
 147       if( retval == 0 && result_ptr && *result_ptr ) {
 148         SQ_free_result( *result_ptr );
 149         *result_ptr = NULL;
 150       }
 151     
 152       tempcon = SQ_duplicate_connection(*sql_connection);
 153     
 154       ER_dbg_va(FAC_QI, ASP_QI_LAST_DET,
 155                 "rtc: closing SQL thread %d", (*sql_connection)->thread_id);
 156       SQ_close_connection(*sql_connection);
 157     
 158       *sql_connection = tempcon;
 159       ER_dbg_va(FAC_QI, ASP_QI_LAST_DET,
 160                 "rtc: reopened as thread %d", (*sql_connection)->thread_id);
 161     
 162       /* make it look as if there was no error and 
 163          the result is empty */
 164       retval = 0;
 165     } /* if watchdog set rtc */
 166   
 167   } /* if rtc not set before */
 168 
 169   return retval; 
 170 }
 171 
 172 /* create_name_query() */
 173 /*++++++++++++++++++++++++++++++++++++++
 174   Create an sql query for the names table. 
 175 
 176   char *query_str
 177 
 178   const char *sql_query
 179 
 180   const char *keys
 181    
 182   More:
 183   +html+ <PRE>
 184   Authors:
 185   ottrey
 186   +html+ </PRE><DL COMPACT>
 187   +html+ <DT>Online References:
 188   +html+ <DD><UL>
 189   +html+ </UL></DL>
 190 
 191   ++++++++++++++++++++++++++++++++++++++*/
 192 static void create_name_query(char *query_str, const char *sql_query, const char *keys) {
     /* [<][>][^][v][top][bottom][index][help] */
 193   int i;
 194   /* Allocate stuff */
 195   GString *from_clause = g_string_sized_new(STR_L);
 196   GString *where_clause = g_string_sized_new(STR_L);
 197   gchar **words = g_strsplit(keys, " ", 0);
 198 
 199   /* double quotes " are used in queries to allow querying for 
 200      names like O'Hara */
 201 
 202   g_string_sprintfa(from_clause, "names N%.2d", 0);
 203   g_string_sprintfa(where_clause, "N%.2d.name=\"%s\"", 0, words[0]);
 204 
 205   for (i=1; words[i] != NULL; i++) {
 206     g_string_sprintfa(from_clause, ", names N%.2d", i);
 207     g_string_sprintfa(where_clause, " AND N%.2d.name=\"%s\" AND N00.object_id = N%.2d.object_id", i, words[i], i);
 208   }
 209 
 210   sprintf(query_str, sql_query, from_clause->str, where_clause->str);
 211 
 212   /* Free up stuff */
 213   g_strfreev(words);
 214   g_string_free(where_clause,/* CONSTCOND */ TRUE);
 215   g_string_free(from_clause, /* CONSTCOND */ TRUE);
 216 
 217 } /* create_name_query() */
 218 
 219 /*+ create_asblock_query: 
 220 
 221   given a string like: AS1
 222                        AS1 - AS10
 223                        AS1-AS10
 224   construct a range query for the as_block table
 225 */
 226 static int create_asblock_query(char *query_str, 
     /* [<][>][^][v][top][bottom][index][help] */
 227                                 const char *sql_query, 
 228                                 const char *keys) {
 229   char *keycopy = wr_string(keys);
 230   char *token, *cursor = keycopy;
 231   int  asnums[2] = {0,0};
 232   int index = 0; /* index into the asnums array */
 233 
 234 
 235   while( (token = strsep( &cursor, "-" )) != NULL && index < 2) {  
 236     /* discard the letters (or leading whitespace), take the number */
 237     if( sscanf(token, "%*[ AS]%d", &asnums[index++]) < 1 ) {
 238       return -1; /* error */
 239     }
 240   }
 241   /* if only beginning was supplied, copy it as end */
 242   if( index == 1 ) {
 243     asnums[1] = asnums[0];
 244   }
 245   
 246   /* now construct the query */
 247   sprintf(query_str, sql_query, asnums[0], asnums[1]);
 248 
 249   wr_free(keycopy);
 250   return 0;
 251 }
 252 
 253 static void add_filter(char *query_str, const Query_command *qc) {
     /* [<][>][^][v][top][bottom][index][help] */
 254   int i;
 255   int qlen;
 256   char filter_atom[STR_M];
 257 
 258 /*
 259   if (MA_bitcount(qc->object_type_bitmap) > 0) { 
 260     g_string_sprintfa(query_str, " AND (");
 261     for (i=0; i < C_END; i++) {
 262       if (MA_isset(qc->object_type_bitmap, i)) {
 263         g_string_sprintfa(query_str, "i.object_type = %d OR ", DF_get_class_dbase_code(i));
 264       }
 265     }
 266     g_string_truncate(query_str, query_str->len-3);
 267     g_string_append_c(query_str, ')');
 268   }
 269 */
 270   if (MA_bitcount(qc->object_type_bitmap) > 0) { 
 271     strcat(query_str, " AND (");
 272     for (i=0; i < C_END; i++) {
 273       if (MA_isset(qc->object_type_bitmap, i)) {
 274         strcpy(filter_atom, "");
 275         sprintf(filter_atom, "i.object_type = %d OR ", i);
 276                         /* XXX class codes should be used instead:
 277                            DF_get_class_dbase_code(i)) 
 278                            but currently the tables contain values of enums
 279                            (C_IN, etc) and not codes
 280                         */
 281         strcat(query_str, filter_atom);
 282       }
 283     }
 284     qlen = strlen(query_str);
 285     query_str[qlen-3] = ')';
 286     query_str[qlen-2] = '\0';
 287     query_str[qlen-1] = '\0';
 288   }
 289   
 290 } /* add_filter() */
 291 
 292 /* create_query() */
 293 /*++++++++++++++++++++++++++++++++++++++
 294   Create an sql query from the query_command and the matching keytype and the
 295   selected inverse attributes.
 296   Note this clears the first inv_attribute it sees, so is called sequentially
 297   until there are no inv_attributes left.
 298 
 299   WK_Type keytype The matching keytype.
 300 
 301   const Query_command *qc The query command.
 302 
 303   mask_t *inv_attrs_bitmap The selected inverse attributes.
 304    
 305   More:
 306   +html+ <PRE>
 307   Authors:
 308         ottrey
 309   +html+ </PRE><DL COMPACT>
 310   +html+ <DT>Online References:
 311   +html+ <DD><UL>
 312   +html+ </UL></DL>
 313 
 314   ++++++++++++++++++++++++++++++++++++++*/
 315 static char *create_query(const Query_t q, const Query_command *qc) {
     /* [<][>][^][v][top][bottom][index][help] */
 316   char *result=NULL;
 317   char result_buff[STR_XL];
 318   Q_Type_t querytype;
 319   int addquery = 0; /* controls if the query should be added to the list */
 320 
 321   if (MA_bitcount(qc->inv_attrs_bitmap) > 0) {
 322     querytype = Q_INVERSE;
 323   }
 324   else {
 325     querytype = Q_LOOKUP;
 326   }
 327 
 328   if ( (q.query != NULL) 
 329     && (q.querytype == querytype) ) {
 330     
 331     addquery = 1; /* if it got here, it should be added, unless.(see asblock)*/
 332     
 333     if (q.keytype == WK_NAME) { 
 334       /* Name queries require special treatment. */
 335        create_name_query(result_buff, q.query, qc->keys);
 336     }
 337     else if( q.keytype == WK_IPADDRESS ) {  /* ifaddr sql lookups */
 338         ip_range_t myrang;
 339         unsigned   begin, end;
 340         ip_keytype_t key_type;
 341 
 342         if (NOERR(IP_smart_range(qc->keys, &myrang, IP_EXPN, &key_type))) {
 343             if(IP_rang_b2_space(&myrang) == IP_V4 ) {
 344                 IP_rang_b2v4(&myrang, &begin, &end);
 345                 sprintf(result_buff, q.query, begin, end);
 346             }
 347             else {
 348                 die;
 349             }
 350         }
 351     }
 352     else if( q.keytype == WK_ASRANGE ) {   /* as_block range composition */
 353       if( create_asblock_query(result_buff, q.query, qc->keys) != 0 ) {
 354         addquery = 0; /* ... unless it's not correct */
 355       }
 356     }
 357     else {
 358       sprintf(result_buff, q.query, qc->keys);
 359     }
 360 
 361     if (q.class == -1 && addquery == 1 ) {
 362       /* It is class type ANY so add the object filtering */
 363       add_filter(result_buff, qc);
 364     }
 365   }
 366   
 367   if( addquery == 1 ) {
 368     dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK);  
 369     strcpy(result, result_buff);
 370     return result;
 371   } 
 372   else {
 373     return NULL;
 374   }
 375 } /* create_query() */
 376 
 377 /* fast_output() */
 378 /*++++++++++++++++++++++++++++++++++++++
 379   This is for the '-F' flag.
 380   It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
 381 
 382   Fast isn't fast anymore - it's just there for compatibility reasons.
 383   This could be speed up if there were breaks out of the loops, once it matched something.
 384   (Wanna add a goto Marek?  :-) ).
 385 
 386   const char *string The string to be "fast outputed".
 387    
 388   More:
 389   +html+ <PRE>
 390   Authors:
 391         ottrey
 392   +html+ </PRE><DL COMPACT>
 393   +html+ <DT>Online References:
 394   +html+ <DD><UL>
 395   +html+ </UL></DL>
 396 
 397   ++++++++++++++++++++++++++++++++++++++*/
 398 
 399 char *fast_output(const char *str) 
     /* [<][>][^][v][top][bottom][index][help] */
 400 {
 401 int i,j;
 402 char *result;
 403 char result_bit[STR_L];
 404 char result_buff[STR_XL];
 405 gchar **lines = g_strsplit(str, "\n", 0);
 406 char * const *attribute_names;
 407 gboolean filtering_an_attribute = FALSE;
 408 char *value;
 409 
 410 attribute_names = DF_get_attribute_names();
 411 
 412 strcpy(result_buff, "");
 413  for (j=0; lines[j] != NULL; j++) {
 414    for(i=0; attribute_names[i] != NULL; i++) {
 415      if (strncmp(attribute_names[i], lines[j], strlen(attribute_names[i])) == 0) {
 416        strcpy(result_bit, "");
 417        /* This is the juicy bit that converts the likes of; "source: RIPE" to "*so: RIPE" */
 418        value = strchr(lines[j], ':');
 419        value++;
 420        /* Now get rid of whitespace. */
 421        while (*value == ' ' || *value == '\t') {
 422          value++;
 423        }
 424        sprintf(result_bit, "*%s: %s\n", DF_get_attribute_code(i), value);
 425        strcat(result_buff, result_bit);
 426      }
 427      /* CONSTCOND */
 428      else if (filtering_an_attribute == TRUE) {
 429        switch (lines[j][0]) {
 430        case ' ':
 431        case '\t':
 432        case '+':
 433          strcpy(result_bit, "");
 434          sprintf(result_bit, "%s\n", lines[j]);
 435          strcat(result_buff, result_bit);
 436          break;
 437          
 438        default:
 439          filtering_an_attribute = FALSE;
 440        }
 441      }
 442    }
 443  }
 444  
 445 
 446  dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK);
 447 
 448  strcpy(result, result_buff);
 449  
 450  return result;
 451 } /* fast_output() */
 452 
 453 /* filter() */
 454 /*++++++++++++++++++++++++++++++++++++++
 455   Basically it's for the '-K' flag for non-set (and non-radix) objects.
 456   It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
 457 
 458   This could be speed up if there were breaks out of the loops, once it matched something.
 459   (Wanna add a goto Marek?  :-) ).
 460 
 461   const char *string The string to be filtered.
 462    
 463   More:
 464   +html+ <PRE>
 465   Authors:
 466         ottrey
 467   +html+ </PRE><DL COMPACT>
 468   +html+ <DT>Online References:
 469   +html+ <DD><UL>
 470   +html+ </UL></DL>
 471 
 472   ++++++++++++++++++++++++++++++++++++++*/
 473 char *filter(const char *str) {
     /* [<][>][^][v][top][bottom][index][help] */
 474   int i,j, passed=0;
 475   char *result;
 476   char result_bit[STR_L];
 477   char result_buff[STR_XL];
 478   gchar **lines = g_strsplit(str, "\n", 0);
 479   char * const *filter_names;
 480   gboolean filtering_an_attribute = FALSE;
 481   
 482   filter_names = DF_get_filter_names();
 483 
 484   strcpy(result_buff, "");
 485   for (i=0; filter_names[i] != NULL; i++) {
 486     for (j=0; lines[j] != NULL; j++) {
 487       if (strncmp(filter_names[i], lines[j], strlen(filter_names[i])) == 0) {
 488         strcpy(result_bit, "");
 489         sprintf(result_bit, "%s\n", lines[j]);
 490         strcat(result_buff, result_bit);
 491         passed++;
 492         
 493         /* CONSTCOND */
 494         filtering_an_attribute = TRUE;
 495       }
 496       /* CONSTCOND */
 497       else if (filtering_an_attribute == TRUE) {
 498         switch (lines[j][0]) {
 499           case ' ':
 500           case '\t':
 501           case '+':
 502             strcpy(result_bit, "");
 503             sprintf(result_bit, "%s\n", lines[j]);
 504             strcat(result_buff, result_bit);
 505           break;
 506 
 507           default:
 508             filtering_an_attribute = FALSE;
 509         }
 510       }
 511     }
 512   }
 513 
 514   if(passed) {
 515     strcat(result_buff, "\n");
 516   }
 517 
 518   dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK);
 519   strcpy(result, result_buff);
 520 
 521   return result;
 522 } /* filter() */
 523 
 524 /* write_results() */
 525 /*++++++++++++++++++++++++++++++++++++++
 526   Write the results to the client socket.
 527 
 528   SQ_result_set_t *result The result set returned from the sql query.
 529   unsigned filtered       if the objects should go through a filter (-K)
 530   sk_conn_st *condat      Connection data for the client    
 531 
 532   More:
 533   +html+ <PRE>
 534   Authors:
 535         ottrey
 536         marek
 537   +html+ </PRE><DL COMPACT>
 538   +html+ <DT>Online References:
 539   +html+ <DD><UL>
 540   +html+ </UL></DL>
 541 
 542   ++++++++++++++++++++++++++++++++++++++*/
 543 static int write_results(SQ_result_set_t *result, 
     /* [<][>][^][v][top][bottom][index][help] */
 544                          unsigned filtered,
 545                          unsigned fast,
 546                          sk_conn_st *condat,
 547                          acc_st    *acc_credit,
 548                          acl_st    *acl
 549                          ) {
 550   SQ_row_t *row;
 551   char *str;
 552   char *filtrate;
 553   char *fasted;
 554   int retrieved_objects=0;
 555   char *objt;
 556   int type;
 557 
 558   /* Get all the results - one at a time */
 559   if (result != NULL) {
 560     /* here we are making use of the mysql_store_result capability
 561        of interrupting the cycle of reading rows. mysql_use_result
 562        would not allow that, would have to be read until end */
 563     
 564     while ( condat->rtc == 0 
 565             && AC_credit_isdenied( acc_credit ) == 0
 566             && (row = SQ_row_next(result)) != NULL ) {
 567       
 568       if (  (str = SQ_get_column_string(result, row, 0)) == NULL
 569             || (objt = SQ_get_column_string(result, row, 3)) == NULL )  { 
 570         /* handle it somehow ? */
 571         die; 
 572       }
 573       else  { 
 574         /* get + add object type */
 575         type = atoi(objt);
 576         
 577         /* ASP_QI_LAST_DET */
 578         ER_dbg_va(FAC_QI, ASP_QI_LAST_DET,
 579                   "Retrieved serial id = %d , type = %s", atoi(str), objt);
 580         
 581         wr_free(str);
 582         wr_free(objt);
 583       }
 584       
 585       /* decrement credit for accounting purposes */
 586       AC_count_object( acc_credit, acl, 
 587                        type == C_PN || type == C_RO ); /* is private? */
 588 
 589       /* break the loop if the credit has just been exceeded and 
 590          further results denied */
 591       if( AC_credit_isdenied( acc_credit ) ) {
 592         continue; 
 593       }
 594       
 595       if ((str = SQ_get_column_string(result, row, 2)) == NULL) { die; } 
 596       else {
 597         
 598         /* The fast output stage */
 599         if (fast == 1) {
 600           fasted = fast_output(str);
 601           wr_free(str);
 602           str = fasted;
 603         }
 604         
 605         /* The filtering stage */
 606         if (filtered == 0) {
 607           SK_cd_puts(condat, str);
 608           SK_cd_puts(condat, "\n");
 609         }
 610         else { 
 611           
 612           /* XXX accounting should be done AFTER filtering, not to count
 613              objects filtered out */
 614 
 615           filtrate = filter(str);
 616           SK_cd_puts(condat, filtrate);
 617           wr_free(filtrate);
 618         }
 619         retrieved_objects++;
 620       }
 621       wr_free(str);
 622     }
 623   }
 624   
 625   return retrieved_objects;
 626 } /* write_results() */
 627 
 628 /* write_objects() */
 629 /*++++++++++++++++++++++++++++++++++++++
 630   This is linked into MySQL by the fact that MySQL doesn't have sub selects
 631   (yet).  The queries are done in two stages.  Make some temporary tables and
 632   insert into them.  Then use them in the next select.
 633 
 634   SQ_connection_t *sql_connection The connection to the database.
 635 
 636   char *id_table The id of the temporary table (This is a result of the hacky
 637                   way we've tried to get MySQL to do sub-selects.)
 638 
 639   sk_conn_st *condat  Connection data for the client
 640 
 641   More:
 642   +html+ <PRE>
 643   Authors:
 644         ottrey
 645   +html+ </PRE><DL COMPACT>
 646   ++++++++++++++++++++++++++++++++++++++*/
 647 static void write_objects(SQ_connection_t **sql_connection, 
     /* [<][>][^][v][top][bottom][index][help] */
 648                           char *id_table, 
 649                           unsigned int filtered, 
 650                           unsigned int fast, 
 651                           sk_conn_st *condat,
 652                           acc_st    *acc_credit,
 653                           acl_st    *acl
 654                           ) 
 655 {
 656   SQ_result_set_t *result = NULL;
 657   int retrieved_objects=0;
 658   char sql_command[STR_XL];  
 659 #if 0
 660   SQ_result_set_t *order_res;
 661   SQ_row_t *order_row;
 662 
 663   SQ_execute_query( *sql_connection, "SELECT object_type FROM object_order ORDER BY order_code", &order_res );
 664   while( (order_row = SQ_row_next(order_res)) != NULL ) {
 665     char *object_type = SQ_get_column_string(order_res, order_row, 0); 
 666     sprintf(sql_command, Q_OBJECTS, id_table, object_type);
 667     
 668     exec/write
 669   }
 670   SQ_free_result(order_res); 
 671 #endif
 672 
 673   sprintf(sql_command, Q_OBJECTS, id_table);
 674 
 675   dieif(sql_execute_watched(condat, sql_connection, sql_command, &result) == -1 );
 676   
 677   /* Problem: if the query was aborted, the result structure does not
 678      refer to any existing connection anymore. So we check rtc here.
 679   */
 680   
 681   if( condat->rtc == 0) {
 682     retrieved_objects = write_results(result, filtered, fast, condat, 
 683                                       acc_credit, acl);
 684     SQ_free_result(result); 
 685   }
 686 } /* write_objects() */
 687 
 688 /* insert_radix_serials() */
 689 /*++++++++++++++++++++++++++++++++++++++
 690   Insert the radix serial numbers into a temporary table in the database.
 691 
 692   mask_t bitmap The bitmap of attribute to be converted.
 693    
 694   SQ_connection_t *sql_connection The connection to the database.
 695 
 696   char *id_table The id of the temporary table (This is a result of the hacky
 697                   way we've tried to get MySQL to do sub-selects.)
 698   
 699   GList *datlist The list of data from the radix tree.
 700 
 701   More:
 702   +html+ <PRE>
 703   Authors:
 704         ottrey
 705   +html+ </PRE><DL COMPACT>
 706   +html+ <DT>Online References:
 707   +html+ <DD><UL>
 708              <LI><A HREF="http://www.gtk.org/rdp/glib/glib-doubly-linked-lists.html">Glist</A>
 709   +html+ </UL></DL>
 710 
 711   ++++++++++++++++++++++++++++++++++++++*/
 712 static void insert_radix_serials(sk_conn_st *condat,
     /* [<][>][^][v][top][bottom][index][help] */
 713                                  SQ_connection_t *sql_connection, 
 714                                  char *id_table, GList *datlist) {
 715   GList    *qitem;
 716   char sql_command[STR_XL];
 717   int serial;
 718 
 719   for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
 720     rx_datcpy_t *datcpy = qitem->data;
 721 
 722     serial = datcpy->leafcpy.data_key;
 723 
 724     sprintf(sql_command, "INSERT INTO %s values (%d)", id_table, serial);
 725     dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1);
 726 
 727     wr_free(datcpy->leafcpy.data_ptr);
 728 
 729     if(condat->rtc != 0) {
 730       break;
 731     }
 732   }
 733 
 734   wr_clear_list( &datlist );
 735 
 736 } /* insert_radix_serials() */
 737 
 738 
 739 /* write_radix_immediate() */
 740 /*++++++++++++++++++++++++++++++++++++++
 741   Display the immediate data carried with the objects returned by the
 742   radix tree.
 743 
 744   GList *datlist      The linked list of dataleaf copies
 745   sk_conn_st *condat  Connection data for the client
 746   acc_st  *acc_credit Accounting struct
 747 
 748 More:
 749   +html+ <PRE>
 750   Authors:
 751         marek
 752   +html+ </PRE><DL COMPACT>
 753   +html+ <DT>Online References:
 754   +html+ <DD><UL>
 755   +html+ </UL></DL>
 756   
 757 
 758   Also free the list of answers.
 759 */
 760 static void write_radix_immediate(GList *datlist, 
     /* [<][>][^][v][top][bottom][index][help] */
 761                                   sk_conn_st *condat,
 762                                   acc_st    *acc_credit,
 763                                   acl_st    *acl) 
 764 {
 765   GList    *qitem;
 766   
 767   for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
 768     rx_datcpy_t *datcpy = qitem->data;
 769 
 770     SK_cd_puts(condat, datcpy->leafcpy.data_ptr );
 771     SK_cd_puts(condat, "\n");
 772     
 773     wr_free(datcpy->leafcpy.data_ptr);
 774     
 775     AC_count_object(acc_credit, acl, 0 /* public object (private=0) */ );
 776 
 777     if(condat->rtc != 0) {
 778       break;
 779     }
 780   }
 781   
 782   wr_clear_list( &datlist );
 783 } /* write_radix_immediate() */
 784 
 785 
 786 /* map_qc2rx() */
 787 /*++++++++++++++++++++++++++++++++++++++
 788   The mapping between a query_command and a radix query.
 789 
 790   Query_instruction *qi The Query Instruction to be created from the mapping
 791                         of the query command.
 792 
 793   const Query_command *qc The query command to be mapped.
 794 
 795   More:
 796   +html+ <PRE>
 797   Authors:
 798         ottrey
 799   +html+ </PRE><DL COMPACT>
 800   +html+ <DT>Online References:
 801   +html+ <DD><UL>
 802   +html+ </UL></DL>
 803 
 804   ++++++++++++++++++++++++++++++++++++++*/
 805 static int map_qc2rx(Query_instruction *qi, const Query_command *qc) {
     /* [<][>][^][v][top][bottom][index][help] */
 806   int result=1;
 807 
 808   qi->rx_keys = qc->keys;
 809 
 810   if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
 811     qi->rx_srch_mode = RX_SRCH_EXLESS;
 812       qi->rx_par_a = 0;
 813   }
 814   else if ( (qc->L == 1) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
 815     qi->rx_srch_mode = RX_SRCH_LESS;
 816     qi->rx_par_a = RX_ALL_DEPTHS;
 817   }
 818   else if ( (qc->L == 0) && (qc->M == 1) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
 819     qi->rx_srch_mode = RX_SRCH_MORE;
 820       qi->rx_par_a = RX_ALL_DEPTHS;
 821   }
 822   else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 1) && (qc->m == 0) && (qc->x == 0) ) {
 823     qi->rx_srch_mode = RX_SRCH_LESS;
 824     qi->rx_par_a = 1;
 825   }
 826   else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 1) && (qc->x == 0) ) {
 827     qi->rx_srch_mode = RX_SRCH_MORE;
 828     qi->rx_par_a = 1;
 829   }
 830   else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 1) ) {
 831     qi->rx_srch_mode = RX_SRCH_EXACT;
 832     qi->rx_par_a = 0;
 833   }
 834   else {
 835       /* user error  (this should have been checked before) */
 836       
 837       ER_dbg_va(FAC_QI, ASP_QI_SKIP, 
 838                 "ERROR in qc2rx mapping: bad combination of flags");
 839       result = 0;
 840   }
 841   
 842   return result;
 843   
 844 } /* map_qc2rx() */
 845 
 846 
 847 /* run_referral() */
 848 /*
 849    invoked when no such domain found. Goes through the domain table
 850    and searches for shorter domains, then if it finds one with referral 
 851    it performs it, otherwise it just returns nothing.
 852 
 853    to perform referral, it actually composes the referral query 
 854    for a given host/port/type and calls the whois query function.
 855 
 856    Well, it returns nothing anyway (void). It just prints to the socket.
 857 
 858 */
 859 void run_referral(Query_environ *qe, 
     /* [<][>][^][v][top][bottom][index][help] */
 860                   char *ref_host,
 861                   int  ref_port_int,
 862                   char *ref_type,
 863                   char *qry)
 864 {
 865   
 866 
 867         /* WH_sock(sock, host, port, query, maxlines, timeout)) */
 868   switch( WH_sock(qe->condat.sock, ref_host, ref_port_int, qry,  25, 5) ) {
 869   case WH_TIMEOUT:
 870     SK_cd_puts(&(qe->condat),"referral timeout\n");
 871     break;
 872             
 873   case WH_MAXLINES:
 874     SK_cd_puts(&(qe->condat),"referral maxlines exceeded\n");
 875     break;
 876             
 877   case WH_BADHOST:
 878     SK_cd_puts(&(qe->condat),"referral host not found\n");
 879     break;
 880 
 881   case WH_CONNECT:
 882     SK_cd_puts(&(qe->condat),"referral host not responding\n");
 883     break;
 884 
 885   case WH_BIND:
 886   case WH_SOCKET:
 887     /* XXX internal server problem... what to do - wait ? */
 888   default:
 889     ;
 890   } /*switch WH_sock */
 891 
 892   
 893 }/*run_referral*/
 894 
 895 static int
 896 qi_collect_domain(char *sourcename,
     /* [<][>][^][v][top][bottom][index][help] */
 897                   SQ_connection_t *sql_connection, 
 898                   char *id_table,
 899                   char *sub_table,
 900                   Query_instructions *qis,   
 901                   Query_environ *qe, 
 902                   Query_instruction *qi,
 903                   acc_st *acc_credit)
 904 {
 905   char *dot = qis->qc->keys;
 906   int subcount = 0;
 907   int foundcount = 0;
 908 
 909   /* while nothing found and still some pieces of the name left */
 910   while( dot != NULL && subcount == 0 ) { 
 911     int refcount = 0;
 912     SQ_row_t *row;
 913     SQ_result_set_t *result = NULL;
 914     char sql_command[STR_XL];
 915 
 916     ER_dbg_va(FAC_QI, ASP_QI_REF_DET, "run_referral: checking %s", dot);
 917 
 918     /* domain lookup -- query into the _S table */
 919     sprintf(sql_command, "INSERT INTO %s SELECT object_id FROM domain WHERE domain = '%s'", sub_table, dot);
 920     
 921     dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1);
 922     subcount = SQ_get_affected_rows(sql_connection); 
 923         
 924     /* referral check */    
 925     sprintf(sql_command, "SELECT type, port, host FROM %s ID, refer WHERE ID.id = refer.object_id", sub_table);
 926     dieif( SQ_execute_query(sql_connection, sql_command, &result) == -1);
 927     refcount = SQ_num_rows(result);
 928     
 929     /* if referral disabled, pass what's in _S and quit */
 930     if( qis->qc->R == 1 ) {
 931       sprintf(sql_command, "INSERT INTO %s SELECT id FROM %s", 
 932               id_table, sub_table);
 933       dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1);
 934       foundcount = SQ_get_affected_rows(sql_connection); 
 935 
 936       dot = NULL; 
 937     } 
 938     else {
 939         if(  refcount != 0      /* domain found and has referral in it */
 940            || Query[qi->queryindex].querytype == Q_INVERSE /* or inverse */
 941            ) {
 942           /* get the referral parameters and perform it 
 943              foreach domain with 'refer' found in this step 
 944              (can be many on eg. "-i nserver very.important.server" )
 945           */
 946           while( (row = SQ_row_next(result)) != NULL) {
 947             char *ref_host = SQ_get_column_string(result, row, 2);
 948             char *ref_type = SQ_get_column_string(result, row, 0);
 949             char *ref_port = SQ_get_column_string(result, row, 1);
 950             int  ref_port_int;
 951             char querystr[STR_L];
 952 
 953             /* get the integer value, it should be correct */
 954             if( sscanf( ref_port, "%d",&ref_port_int) < 1 ) {
 955               die;
 956             }
 957             
 958             strcpy(querystr,"");
 959             
 960             /* put -r if the reftype is RIPE and -r or -i were used */
 961             if( strcmp(ref_type,"RIPE") == 0 
 962                 && ( Query[qi->queryindex].querytype == Q_INVERSE       
 963                        || qis->recursive > 0  )   ) {
 964               strcat(querystr," -r ");
 965             }
 966             
 967             /* prepend with -Vversion,IP for type CLIENTADDRESS */
 968             if( strcmp(ref_type,"CLIENTADDRESS") == 0 ) {
 969               char optv[STR_M];
 970               
 971               snprintf(optv,STR_M," -V%s,%s ",VERSION, qe->condat.ip);
 972               strcat(querystr,optv);
 973             }
 974             
 975             /* now set the search term - set to the stripped down version 
 976                for inverse query, full-length otherwise */
 977             if( Query[qi->queryindex].querytype == Q_INVERSE ) {
 978               strcat(querystr, dot);
 979             }
 980             else {
 981               strcat(querystr, qis->qc->keys);
 982             }
 983             
 984             SK_cd_printf(&(qe->condat), 
 985                "%% The object shown below is NOT in the %s database.\n"
 986                "%% It has been obtained by querying a remote server:\n"
 987                "%% (%s) at port %d.\n"
 988                "%% To see the object stored in the %s database\n"
 989                "%% use the -R flag in your query\n"
 990                "%%\n"
 991                "%%%%%% Start of referred query result\n\n",
 992                sourcename, 
 993                ref_host, ref_port_int,
 994                sourcename );
 995 
 996             /* do the referral */
 997             ER_dbg_va(FAC_QI, ASP_QI_REF_GEN, "referral host is %s", ref_host); 
 998             
 999             run_referral( qe, ref_host, ref_port_int, ref_type, querystr);
1000             
1001             SK_cd_puts(&(qe->condat), "\n%%% End of referred query result\n");
1002             acc_credit->referrals -= 1;
1003             dot = NULL; /* don't make another round */
1004 
1005           } /* foreach domain with 'refer' found in this step */
1006         }
1007     } /* if not disabled by -R */
1008   
1009     SQ_free_result(result);
1010     result = NULL;
1011 
1012     if( dot != NULL && (dot=index(dot,'.')) != NULL) {
1013       dot++;
1014     }
1015   }
1016     
1017   return foundcount;
1018 } /* check_domain */
1019 
1020 static
1021 void 
1022 add_ref_name(SQ_connection_t *sql_connection, 
     /* [<][>][^][v][top][bottom][index][help] */
1023              char *rectable,
1024              char *allnames
1025              )
1026 {
1027   /* construct the query, allow zero-length list */
1028   if( strlen(allnames) > 0 ) {
1029     char final_query[STR_XL];
1030     char select_query[STR_XL];
1031 
1032     create_name_query(select_query, "SELECT N00.object_id FROM %s WHERE %s "
1033                       "AND N00.object_type != 100 AND N00.thread_id = 0", 
1034                       allnames);
1035     
1036     sprintf(final_query, "INSERT INTO %s %s",
1037             rectable,
1038             select_query);
1039     
1040     dieif(SQ_execute_query(sql_connection, final_query, NULL) == -1 );
1041 
1042     allnames[0]=0;
1043   }
1044 }
1045 
1046 static
1047 void
1048 qi_collect_ids(ca_dbSource_t *dbhdl,
     /* [<][>][^][v][top][bottom][index][help] */
1049                char *sourcename,
1050                SQ_connection_t **sql_connection,
1051                Query_instructions *qis,
1052                Query_environ *qe,       
1053                char *id_table,
1054                GList **datlist,
1055                acc_st *acc_credit,
1056                acl_st *acl
1057                )
1058 {
1059   Query_instruction **ins=NULL;
1060   int i;
1061   int  refcount = 0, count, errors=0;
1062   char sql_command[STR_XL];
1063   er_ret_t err;
1064   char sub_table[32];
1065   int limit ;
1066              /* a limit on the max number of objects to be returned
1067                 from a single search. For some queries the object types
1068                 are not known at this stage, so the limit must be
1069                 the higher number of the two: private / public,
1070                 or unlimited if any of them is 'unlimited'.
1071              */
1072   char limit_str[32];
1073 
1074   if( (limit = AC_get_higher_limit(acc_credit,acl)) == -1) {
1075     strcpy(limit_str,"");
1076   } else {
1077     sprintf(limit_str," LIMIT %d", limit+1); /* make sure we collect more
1078                                                 so that the client hits
1079                                                 the limit */
1080   }
1081 
1082   sprintf(sub_table, "%s_S ", id_table);
1083   
1084   /* see if there was a leftover table from a crashed session 
1085    * (assume the ID cannot be currently in use)
1086    */
1087   sprintf(sql_command, "DROP TABLE IF EXISTS %s", sub_table);
1088   dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1089 
1090   /* create a table for special subqueries (domain only for now) */
1091   sprintf(sql_command, "CREATE TABLE %s ( id int ) TYPE=HEAP", sub_table);
1092   dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1093   
1094   /* Iterate through query instructions */
1095   ins = qis->instruction;
1096   for (i=0; ins[i] != NULL && errors == 0; i++) {
1097     Query_instruction *qi = ins[i];
1098     
1099     /* check if the client is still there */
1100     if( qe->condat.rtc ) {
1101       break;
1102     }
1103 
1104     switch ( qi->search_type ) {
1105     case R_SQL:
1106       if ( qi->query_str != NULL ) {
1107 
1108         /* handle special cases first */
1109         if( Query[qi->queryindex].class == C_DN 
1110             && Query[qi->queryindex].querytype == Q_LOOKUP ) {
1111           
1112           /* if any more cases than just domain appear, we will be
1113              cleaning the _S table from the previous query here 
1114              
1115              "DELETE FROM %s_S"
1116           */
1117           
1118           count = qi_collect_domain(sourcename, *sql_connection, id_table, 
1119                                     sub_table, qis, qe, qi, acc_credit);
1120         } /* if class DN and Straight lookup */
1121         else {
1122           /* any other class of query */
1123 
1124           sprintf(sql_command, "INSERT INTO %s %s %s", 
1125                   id_table, qi->query_str, limit_str);
1126 
1127           if(sql_execute_watched( &(qe->condat), sql_connection, 
1128                                   sql_command, NULL) == -1 ) {
1129 
1130             ER_perror(FAC_QI, QI_SQLERR," query='%s' [%d] %s", 
1131                       sql_command,
1132                       SQ_errno(*sql_connection), SQ_error(*sql_connection));
1133             errors++;
1134           }
1135           count = SQ_get_affected_rows(*sql_connection);
1136         } /* not DN */
1137       } /* if SQL query not NULL */
1138       
1139       ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1140                 "%d entries added in %s query for %s",
1141                 count, Query[qi->queryindex].descr, qis->qc->keys
1142                 );
1143       break;
1144       
1145     case R_RADIX:
1146 
1147      
1148       err = RP_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0, 
1149                           qi->rx_keys, dbhdl, 
1150                           Query[qi->queryindex].attribute, 
1151                           datlist, limit);
1152      
1153 
1154       if( NOERR(err)) {
1155         if( ER_is_traced(FAC_QI, ASP_QI_COLL_DET ) ) {
1156           /* prevent unnecessary g_list_length call */
1157           
1158           ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1159                     "%d entries after %s (mode %d par %d reg %d) query for %s",
1160                     g_list_length(*datlist),
1161                     Query[qi->queryindex].descr,
1162                     qi->rx_srch_mode, qi->rx_par_a, 
1163                     dbhdl,
1164                     qi->rx_keys);
1165         }
1166       }
1167       else {
1168         ER_inf_va(FAC_QI, ASP_QI_COLL_DET,
1169                   "RP_asc_search returned %x ", err);
1170       }
1171       break;
1172       
1173     default: die;
1174     } /* switch */
1175     
1176   } /* for <every instruction> */
1177 
1178   /* Now drop the _S table */
1179   sprintf(sql_command, "DROP TABLE %s", sub_table);
1180   dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1181 
1182 }
1183 
1184 static
1185 void
1186 qi_fetch_references(SQ_connection_t **sql_connection,
     /* [<][>][^][v][top][bottom][index][help] */
1187                     Query_environ *qe,
1188                     char *id_table,
1189                     acc_st *acc_credit,
1190                     acl_st *acl
1191                     )
1192 {
1193 char rec_table[32];
1194     SQ_result_set_t *result = NULL;
1195     SQ_row_t *row;
1196     int thisid = 0;
1197     int oldid = 0;
1198     char allnames[STR_M];
1199     char sql_command[STR_XL];
1200  
1201     sprintf(rec_table, "%s_R", id_table);
1202     
1203     /* see if there was a leftover table from a crashed session 
1204      * (assume the ID cannot be currently in use)
1205      */
1206     sprintf(sql_command, "DROP TABLE IF EXISTS %s ", rec_table);
1207     dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1208 
1209     /* a temporary table for recursive data must be created, because
1210        a query using the same table as a source and target is illegal
1211        ( like: INSERT into ID_123 SELECT * FROM ID_123,admin_c WHERE ... )
1212     */
1213     sprintf(sql_command, "CREATE TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", rec_table);
1214     dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1215     
1216     /* find the contacts */      
1217     sprintf(sql_command, Q_REC, rec_table, id_table, "author");
1218     dieif(sql_execute_watched( &(qe->condat), sql_connection, sql_command, NULL) == -1 );
1219     
1220     sprintf(sql_command, Q_REC, rec_table, id_table, "admin_c");
1221     dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, NULL) == -1 );
1222     
1223     sprintf(sql_command, Q_REC, rec_table, id_table, "tech_c" );
1224     dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, NULL) == -1 );
1225     
1226     sprintf(sql_command, Q_REC, rec_table, id_table, "zone_c" );
1227     dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, NULL) == -1 );
1228     
1229     
1230     /* replace references to dummies by references by name */
1231     sprintf(sql_command, 
1232             " SELECT id, name    FROM %s IDS STRAIGHT_JOIN names "
1233             " WHERE IDS.id = names.object_id "
1234             "      AND names.object_type = 100"
1235             " ORDER BY id",
1236             rec_table);
1237     
1238     dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, 
1239                               &result) == -1 );
1240     /* well, it might not be -1, but if the watchdog worked then the
1241        result is NULL */
1242     if( result != NULL ) {
1243       
1244       allnames[0]=0;
1245       /* now go through the results and collect names */
1246       while ( (qe->condat.rtc == 0)
1247               && (row = SQ_row_next(result)) != NULL ) {
1248         char *id   = SQ_get_column_string(result, row, 0);
1249         char *name = SQ_get_column_string(result, row, 1);
1250         
1251         thisid = atoi(id);
1252         
1253         /* when the id changes, the name is complete */
1254         if( thisid != oldid && oldid != 0 ) {
1255           add_ref_name( *sql_connection, rec_table, allnames);
1256         }
1257         
1258         strcat(allnames, name);
1259         strcat(allnames, " ");
1260         oldid = thisid;
1261         wr_free(id);
1262         wr_free(name);
1263       }
1264       /* also do the last name */
1265       add_ref_name( *sql_connection, rec_table, allnames);
1266       
1267       SQ_free_result(result); /* we can do it only because the watchdog */
1268       /* has not started between the check for non-NULL result and here */
1269     }
1270     
1271     /* now copy things back to the main temporary table   */
1272     sprintf(sql_command, "INSERT INTO %s SELECT * FROM %s", 
1273             id_table, rec_table);
1274     dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1275     
1276     /* Now drop the IDS recursive table */
1277     sprintf(sql_command, "DROP TABLE %s", rec_table);
1278     dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1279 }
1280 
1281 
1282 /* QI_execute() */
1283 /*++++++++++++++++++++++++++++++++++++++
1284   Execute the query instructions.  This is called for each source.
1285 
1286   void *database_voidptr Pointer to the database name
1287   
1288   void *qis_voidptr Pointer to the query_instructions.
1289    
1290   More:
1291   +html+ <PRE>
1292   Authors:
1293         ottrey
1294   +html+ </PRE>
1295   ++++++++++++++++++++++++++++++++++++++*/
1296 er_ret_t QI_execute(ca_dbSource_t *dbhdl,
     /* [<][>][^][v][top][bottom][index][help] */
1297                     Query_instructions *qis, 
1298                     Query_environ *qe,  
1299                     acc_st *acc_credit,
1300                     acl_st *acl
1301                     ) 
1302 {
1303   /* those things must be freed after use! */
1304   char *dbhost = ca_get_srcdbmachine(dbhdl);
1305   char *dbname = ca_get_srcdbname(dbhdl);
1306   char *dbuser = ca_get_srcdbuser(dbhdl);
1307   char *dbpass = ca_get_srcdbpassword(dbhdl);
1308   char *srcnam = ca_get_srcname(dbhdl);
1309   char id_table[STR_S];
1310   char sql_command[STR_XL];
1311   GList *datlist=NULL;
1312   SQ_connection_t *sql_connection=NULL;
1313 
1314   sql_connection = SQ_get_connection( dbhost, ca_get_srcdbport(dbhdl),
1315                                       dbname, dbuser, dbpass );
1316   if (sql_connection == NULL) {
1317     ER_perror(FAC_QI, QI_CANTDB," database='%s' [%d] %s", 
1318               dbname, SQ_errno(sql_connection), SQ_error(sql_connection));
1319     return QI_CANTDB;
1320   }
1321 
1322   sprintf(id_table, "ID_%ld_%d",   mysql_thread_id(sql_connection),
1323           pthread_self());
1324 
1325   /* see if there was a leftover table from a crashed session 
1326    * (assume the ID cannot be currently in use)
1327    */
1328   sprintf(sql_command, "DROP TABLE IF EXISTS %s ", id_table);
1329   dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
1330   
1331   /* create a table for id's of all objects found NOT NULL , UNIQUE(id) */
1332   sprintf(sql_command, "CREATE TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", id_table);
1333   dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
1334 
1335   qi_collect_ids(dbhdl, srcnam, &sql_connection, qis, qe, id_table, 
1336                  &datlist, acc_credit, acl);
1337 
1338   /* post-processing */
1339   if( qis->filtered == 0 ) {
1340     /* start the watchdog just to set the rtc flag */
1341     SK_watchclear(&(qe->condat));
1342     SK_watchstart(&(qe->condat));
1343 
1344     /* add radix results (only if -K is not active) */
1345     insert_radix_serials(&(qe->condat), sql_connection, id_table, datlist);
1346 
1347     SK_watchstop(&(qe->condat));
1348   }
1349 
1350   /* fetch recursive objects (ac,tc,zc,ah) */
1351   if ( qis->recursive ) {
1352     qi_fetch_references( &sql_connection, qe, id_table, acc_credit, acl);
1353   } /* if recursive */
1354   
1355   /* display */
1356   /* -K filtering: 
1357    * right now only filtering, no expanding sets like write_set_objects() 
1358    */
1359   
1360   /* display the immediate data from the radix tree */
1361   if( qis->filtered == 1 ) {
1362     write_radix_immediate(datlist, &(qe->condat), acc_credit, acl );
1363   }
1364 
1365   /* display objects from the IDs table */
1366   write_objects( &sql_connection, id_table, qis->filtered,
1367                 qis->fast, &(qe->condat), acc_credit, acl);
1368 
1369   /* Now drop the IDS table */
1370   sprintf(sql_command, "DROP TABLE %s", id_table);
1371   dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
1372   SQ_close_connection(sql_connection);  
1373 
1374   /* free allocated parameters */
1375   wr_free(dbhost);
1376   wr_free(dbname);
1377   wr_free(dbuser);
1378   wr_free(dbpass);
1379   wr_free(srcnam);
1380 
1381   return QI_OK;
1382 } /* QI_execute() */
1383 
1384 
1385 /* instruction_free() */
1386 /*++++++++++++++++++++++++++++++++++++++
1387   Free the instruction.
1388 
1389   Query_instruction *qi query_instruction to be freed.
1390    
1391   More:
1392   +html+ <PRE>
1393   Authors:
1394         ottrey
1395   +html+ </PRE>
1396   ++++++++++++++++++++++++++++++++++++++*/
1397 static void instruction_free(Query_instruction *qi) {
     /* [<][>][^][v][top][bottom][index][help] */
1398   if (qi != NULL) {
1399     if (qi->query_str != NULL) {
1400       wr_free(qi->query_str);
1401     }
1402     wr_free(qi);
1403   }
1404 } /* instruction_free() */
1405 
1406 /* QI_free() */
1407 /*++++++++++++++++++++++++++++++++++++++
1408   Free the query_instructions.
1409 
1410   Query_instructions *qis Query_instructions to be freed.
1411    
1412   More:
1413   +html+ <PRE>
1414   Authors:
1415         ottrey, marek
1416   +html+ </PRE>
1417   ++++++++++++++++++++++++++++++++++++++*/
1418 void QI_free(Query_instructions *qis) {
     /* [<][>][^][v][top][bottom][index][help] */
1419   int i;
1420 
1421   for (i=0; qis->instruction[i] != NULL; i++) {
1422     instruction_free(qis->instruction[i]);
1423   } 
1424 
1425   if (qis != NULL) {
1426     wr_free(qis);
1427   }
1428 
1429 } /* QI_free() */
1430 
1431 /*++++++++++++++++++++++++++++++++++++++
1432   Determine if this query should be conducted or not.
1433 
1434   If it was an inverse query - it the attribute appears in the query command's bitmap.
1435   If it was a lookup query - if the attribute appears in the object type bitmap or
1436                              disregard if there is no object_type bitmap (Ie object filter).
1437 
1438   mask_t bitmap The bitmap of attribute to be converted.
1439    
1440   const Query_command *qc The query_command that the instructions are created
1441                           from.
1442   
1443   const Query_t q The query being investigated.
1444 
1445   ++++++++++++++++++++++++++++++++++++++*/
1446 static int valid_query(const Query_command *qc, const Query_t q) {
     /* [<][>][^][v][top][bottom][index][help] */
1447   int result=0;
1448 
1449   if (MA_isset(qc->keytypes_bitmap, q.keytype) == 1) {
1450     if (q.query != NULL) {
1451       switch (q.querytype) {
1452         case Q_INVERSE:
1453           if (MA_isset(qc->inv_attrs_bitmap, q.attribute) ) {
1454             result = 1;
1455           }
1456         break;
1457 
1458         case Q_LOOKUP:
1459           if (MA_bitcount(qc->object_type_bitmap) == 0) {
1460             result=1;
1461           }
1462           else if (q.class<0 || MA_isset(qc->object_type_bitmap, q.class)) {
1463             result=1;
1464           }
1465         break;
1466 
1467         default:
1468           fprintf(stderr, "qi:valid_query() -> Bad querytype\n");
1469       }
1470     }
1471   }
1472 
1473   return result;
1474 } /* valid_query() */
1475 
1476 /* QI_new() */
1477 /*++++++++++++++++++++++++++++++++++++++
1478   Create a new set of query_instructions.
1479 
1480   const Query_command *qc The query_command that the instructions are created
1481                           from.
1482 
1483   const Query_environ *qe The environmental variables that they query is being
1484                           performed under.
1485   More:
1486   +html+ <PRE>
1487   Authors:
1488         ottrey
1489   +html+ </PRE>
1490   ++++++++++++++++++++++++++++++++++++++*/
1491 Query_instructions *QI_new(const Query_command *qc, const Query_environ *qe) {
     /* [<][>][^][v][top][bottom][index][help] */
1492   Query_instructions *qis=NULL;
1493   Query_instruction *qi=NULL;
1494   int i_no=0;
1495   int i;
1496   char *query_str;
1497 
1498   dieif(wr_calloc( (void **) & qis, 1, sizeof(Query_instructions)) != UT_OK);
1499 
1500   qis->filtered = qc->filtered;
1501   qis->fast = qc->fast;
1502   qis->recursive = qc->recursive;
1503   qis->qc = (qc);
1504 
1505   
1506   for (i=0; Query[i].query != NULL; i++) {
1507 
1508     /* If a valid query. */
1509     if ( valid_query(qc, Query[i]) == 1) {
1510 
1511       dieif( wr_calloc((void **) &qi, 1, sizeof(Query_instruction)) != UT_OK);
1512 
1513       qi->queryindex = i;
1514 
1515       /* SQL Query */
1516       if ( Query[i].refer == R_SQL) {
1517         qi->search_type = R_SQL;
1518         query_str = create_query(Query[i], qc);
1519 
1520         if (query_str!= NULL) {
1521           qi->query_str = query_str;
1522           qis->instruction[i_no++] = qi;
1523         }
1524       }
1525       /* Radix Query */
1526       else if (Query[i].refer == R_RADIX) {
1527         qi->search_type = R_RADIX;
1528         
1529         if (map_qc2rx(qi, qc) == 1) {
1530           int j;
1531           int found=0;
1532           
1533           /* check that there is no such query yet, for example if
1534              more than one keytype (wk) matched */
1535           for (j=0; j<i_no; j++) {
1536             Query_instruction *qij = qis->instruction[j];
1537             
1538             if(    qij->search_type == R_RADIX
1539                    && Query[qij->queryindex].attribute 
1540                    == Query[qi ->queryindex].attribute) {
1541               
1542               found=1;
1543               break;
1544             }
1545           }
1546           
1547           if ( found ) {
1548             /* Discard the Query Instruction */
1549             wr_free(qi);
1550           } 
1551           else {
1552             /* Add the query_instruction to the array */
1553             qis->instruction[i_no++] = qi;
1554           }
1555         }
1556       }
1557       else {
1558           /* ERROR: bad search_type */
1559           die;
1560       }
1561     }
1562   }
1563   qis->instruction[i_no++] = NULL;
1564 
1565 
1566   {  /* tracing */
1567       char *descrstr = QI_queries_to_string(qis);
1568 
1569       ER_dbg_va(FAC_QI, ASP_QI_COLL_GEN, "Queries: %s", descrstr );
1570       wr_free( descrstr );
1571   }
1572 
1573   return qis;
1574 
1575 } /* QI_new() */
1576 
1577 /* QI_queries_to_string() 
1578    
1579    returns a list of descriptions for queries that will be performed.
1580 */
1581 
1582 char *QI_queries_to_string(Query_instructions *qis)
     /* [<][>][^][v][top][bottom][index][help] */
1583 {
1584    Query_instruction *qi;
1585    int i;
1586    char *resstr = NULL;
1587 
1588    dieif( wr_realloc((void **)&resstr, 2 ) != UT_OK);
1589    strcpy(resstr, "{");
1590 
1591    for( i = 0; ( qi=qis->instruction[i] ) != NULL;  i++ ) {
1592        char *descr = Query[qi->queryindex].descr;
1593        int oldres = strlen( resstr );
1594        
1595        dieif( wr_realloc((void **)&resstr, oldres+strlen(descr)+2) != UT_OK);
1596        strcat(resstr, descr);
1597        strcat(resstr, ",");
1598    }
1599    if( i>0 ) {
1600        /* cancel the last comma */
1601        resstr[strlen(resstr)-1] = 0;
1602    }
1603 
1604    dieif( wr_realloc((void **)&resstr, strlen( resstr ) + 2 ) 
1605           != UT_OK);
1606    strcat(resstr, "}");
1607    
1608    return resstr;
1609 }

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