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. add_ref_name
  16. qi_collect_ids
  17. qi_fetch_references
  18. QI_execute
  19. instruction_free
  20. QI_free
  21. valid_query
  22. QI_new
  23. QI_queries_to_string

   1 /***************************************
   2   $Revision: 1.49 $
   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   XXX NB. this is very dependendant on what rows are returned in the result!!!
 533    
 534   More:
 535   +html+ <PRE>
 536   Authors:
 537         ottrey
 538   +html+ </PRE><DL COMPACT>
 539   +html+ <DT>Online References:
 540   +html+ <DD><UL>
 541   +html+ </UL></DL>
 542 
 543   ++++++++++++++++++++++++++++++++++++++*/
 544 static int write_results(SQ_result_set_t *result, 
     /* [<][>][^][v][top][bottom][index][help] */
 545                          unsigned filtered,
 546                          unsigned fast,
 547                          sk_conn_st *condat,
 548                          acc_st    *acc_credit,
 549                          acl_st    *acl
 550                          ) {
 551   SQ_row_t *row;
 552   char *str;
 553   char *filtrate;
 554   char *fasted;
 555   int retrieved_objects=0;
 556   char *objt;
 557   int type;
 558 
 559   /* Get all the results - one at a time */
 560   if (result != NULL) {
 561     /* here we are making use of the mysql_store_result capability
 562        of interrupting the cycle of reading rows. mysql_use_result
 563        would not allow that, would have to be read until end */
 564     
 565     while ( condat->rtc == 0 
 566             && AC_credit_isdenied( acc_credit ) == 0
 567             && (row = SQ_row_next(result)) != NULL ) {
 568       
 569       if (  (str = SQ_get_column_string(result, row, 0)) == NULL
 570             || (objt = SQ_get_column_string(result, row, 3)) == NULL )  { 
 571         /* handle it somehow ? */
 572         die; 
 573       }
 574       else  { 
 575         /* get + add object type */
 576         type = atoi(objt);
 577         
 578         /* ASP_QI_LAST_DET */
 579         ER_dbg_va(FAC_QI, ASP_QI_LAST_DET,
 580                   "Retrieved serial id = %d , type = %s", atoi(str), objt);
 581         
 582         wr_free(str);
 583         wr_free(objt);
 584       }
 585       
 586       /* decrement credit for accounting purposes */
 587       AC_count_object( acc_credit, acl, 
 588                        type == C_PN || type == C_RO ); /* is private? */
 589 
 590       /* break the loop if the credit has just been exceeded and 
 591          further results denied */
 592       if( AC_credit_isdenied( acc_credit ) ) {
 593         continue; 
 594       }
 595       
 596       if ((str = SQ_get_column_string(result, row, 2)) == NULL) { die; } 
 597       else {
 598         
 599         /* The fast output stage */
 600         if (fast == 1) {
 601           fasted = fast_output(str);
 602           wr_free(str);
 603           str = fasted;
 604         }
 605         
 606         /* The filtering stage */
 607         if (filtered == 0) {
 608           SK_cd_puts(condat, str);
 609           SK_cd_puts(condat, "\n");
 610         }
 611         else { 
 612           
 613           /* XXX accounting should be done AFTER that, and not for objects
 614              filtered out */
 615 
 616           filtrate = filter(str);
 617           SK_cd_puts(condat, filtrate);
 618           wr_free(filtrate);
 619         }
 620         retrieved_objects++;
 621       }
 622       wr_free(str);
 623     }
 624   }
 625   
 626   return retrieved_objects;
 627 } /* write_results() */
 628 
 629 /* write_objects() */
 630 /*++++++++++++++++++++++++++++++++++++++
 631   This is linked into MySQL by the fact that MySQL doesn't have sub selects
 632   (yet).  The queries are done in two stages.  Make some temporary tables and
 633   insert into them.  Then use them in the next select.
 634 
 635   SQ_connection_t *sql_connection The connection to the database.
 636 
 637   char *id_table The id of the temporary table (This is a result of the hacky
 638                   way we've tried to get MySQL to do sub-selects.)
 639 
 640   sk_conn_st *condat  Connection data for the client
 641 
 642   More:
 643   +html+ <PRE>
 644   Authors:
 645         ottrey
 646   +html+ </PRE><DL COMPACT>
 647   ++++++++++++++++++++++++++++++++++++++*/
 648 static void write_objects(SQ_connection_t **sql_connection, 
     /* [<][>][^][v][top][bottom][index][help] */
 649                           char *id_table, 
 650                           unsigned int filtered, 
 651                           unsigned int fast, 
 652                           sk_conn_st *condat,
 653                           acc_st    *acc_credit,
 654                           acl_st    *acl
 655                           ) 
 656 {
 657   /* XXX This should really return a linked list of the objects */
 658 
 659   SQ_result_set_t *result = NULL;
 660   int retrieved_objects=0;
 661   char sql_command[STR_XL];  
 662 #if 0
 663   SQ_result_set_t *order_res;
 664   SQ_row_t *order_row;
 665 
 666   SQ_execute_query( *sql_connection, "SELECT object_type FROM object_order ORDER BY order_code", &order_res );
 667   while( (order_row = SQ_row_next(order_res)) != NULL ) {
 668     char *object_type = SQ_get_column_string(order_res, order_row, 0); 
 669     sprintf(sql_command, Q_OBJECTS, id_table, object_type);
 670     
 671     exec/write
 672   }
 673   SQ_free_result(order_res); 
 674 #endif
 675 
 676   sprintf(sql_command, Q_OBJECTS, id_table);
 677 
 678   dieif(sql_execute_watched(condat, sql_connection, sql_command, &result) == -1 );
 679   
 680   /* Problem: if the query was aborted, the result structure does not
 681      refer to any existing connection anymore. So we check rtc here.
 682   */
 683   
 684   if( condat->rtc == 0) {
 685     retrieved_objects = write_results(result, filtered, fast, condat, 
 686                                       acc_credit, acl);
 687     SQ_free_result(result); 
 688   }
 689 } /* write_objects() */
 690 
 691 /* insert_radix_serials() */
 692 /*++++++++++++++++++++++++++++++++++++++
 693   Insert the radix serial numbers into a temporary table in the database.
 694 
 695   mask_t bitmap The bitmap of attribute to be converted.
 696    
 697   SQ_connection_t *sql_connection The connection to the database.
 698 
 699   char *id_table The id of the temporary table (This is a result of the hacky
 700                   way we've tried to get MySQL to do sub-selects.)
 701   
 702   GList *datlist The list of data from the radix tree.
 703 
 704   XXX Hmmmmm this isn't really a good place to free things... infact it's quite nasty.  :-(
 705   
 706   More:
 707   +html+ <PRE>
 708   Authors:
 709         ottrey
 710   +html+ </PRE><DL COMPACT>
 711   +html+ <DT>Online References:
 712   +html+ <DD><UL>
 713              <LI><A HREF="http://www.gtk.org/rdp/glib/glib-doubly-linked-lists.html">Glist</A>
 714   +html+ </UL></DL>
 715 
 716   ++++++++++++++++++++++++++++++++++++++*/
 717 static void insert_radix_serials(sk_conn_st *condat,
     /* [<][>][^][v][top][bottom][index][help] */
 718                                  SQ_connection_t *sql_connection, 
 719                                  char *id_table, GList *datlist) {
 720   GList    *qitem;
 721   char sql_command[STR_XL];
 722   int serial;
 723 
 724   for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
 725     rx_datcpy_t *datcpy = qitem->data;
 726 
 727     serial = datcpy->leafcpy.data_key;
 728 
 729     sprintf(sql_command, "INSERT INTO %s values (%d)", id_table, serial);
 730     dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1);
 731 
 732     wr_free(datcpy->leafcpy.data_ptr);
 733 
 734     if(condat->rtc != 0) {
 735       break;
 736     }
 737   }
 738 
 739   wr_clear_list( &datlist );
 740 
 741 } /* insert_radix_serials() */
 742 
 743 
 744 /* write_radix_immediate() */
 745 /*++++++++++++++++++++++++++++++++++++++
 746   Display the immediate data carried with the objects returned by the
 747   radix tree.
 748 
 749   GList *datlist      The linked list of dataleaf copies
 750   sk_conn_st *condat  Connection data for the client
 751   acc_st  *acc_credit Accounting struct
 752 
 753 More:
 754   +html+ <PRE>
 755   Authors:
 756         marek
 757   +html+ </PRE><DL COMPACT>
 758   +html+ <DT>Online References:
 759   +html+ <DD><UL>
 760   +html+ </UL></DL>
 761   
 762 
 763   Also free the list of answers.
 764 */
 765 static void write_radix_immediate(GList *datlist, 
     /* [<][>][^][v][top][bottom][index][help] */
 766                                   sk_conn_st *condat,
 767                                   acc_st    *acc_credit,
 768                                   acl_st    *acl) 
 769 {
 770   GList    *qitem;
 771   
 772   for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
 773     rx_datcpy_t *datcpy = qitem->data;
 774 
 775     SK_cd_puts(condat, datcpy->leafcpy.data_ptr );
 776     SK_cd_puts(condat, "\n");
 777     
 778     wr_free(datcpy->leafcpy.data_ptr);
 779     
 780     AC_count_object(acc_credit, acl, 0 /* public object (private=0) */ );
 781 
 782     if(condat->rtc != 0) {
 783       break;
 784     }
 785   }
 786   
 787   wr_clear_list( &datlist );
 788 } /* write_radix_immediate() */
 789 
 790 
 791 /* map_qc2rx() */
 792 /*++++++++++++++++++++++++++++++++++++++
 793   The mapping between a query_command and a radix query.
 794 
 795   Query_instruction *qi The Query Instruction to be created from the mapping
 796                         of the query command.
 797 
 798   const Query_command *qc The query command to be mapped.
 799 
 800   More:
 801   +html+ <PRE>
 802   Authors:
 803         ottrey
 804   +html+ </PRE><DL COMPACT>
 805   +html+ <DT>Online References:
 806   +html+ <DD><UL>
 807   +html+ </UL></DL>
 808 
 809   ++++++++++++++++++++++++++++++++++++++*/
 810 static int map_qc2rx(Query_instruction *qi, const Query_command *qc) {
     /* [<][>][^][v][top][bottom][index][help] */
 811   int result=1;
 812 
 813   qi->rx_keys = qc->keys;
 814 
 815   if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
 816     qi->rx_srch_mode = RX_SRCH_EXLESS;
 817       qi->rx_par_a = 0;
 818   }
 819   else if ( (qc->L == 1) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
 820     qi->rx_srch_mode = RX_SRCH_LESS;
 821     qi->rx_par_a = RX_ALL_DEPTHS;
 822   }
 823   else if ( (qc->L == 0) && (qc->M == 1) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
 824     qi->rx_srch_mode = RX_SRCH_MORE;
 825       qi->rx_par_a = RX_ALL_DEPTHS;
 826   }
 827   else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 1) && (qc->m == 0) && (qc->x == 0) ) {
 828     qi->rx_srch_mode = RX_SRCH_LESS;
 829     qi->rx_par_a = 1;
 830   }
 831   else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 1) && (qc->x == 0) ) {
 832     qi->rx_srch_mode = RX_SRCH_MORE;
 833     qi->rx_par_a = 1;
 834   }
 835   else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 1) ) {
 836     qi->rx_srch_mode = RX_SRCH_EXACT;
 837     qi->rx_par_a = 0;
 838   }
 839   else {
 840       /* user error  ( XXX : this should have been checked before) */
 841       
 842       ER_dbg_va(FAC_QI, ASP_QI_SKIP, 
 843                 "ERROR in qc2rx mapping: bad combination of flags");
 844       result = 0;
 845   }
 846   
 847   return result;
 848   
 849 } /* map_qc2rx() */
 850 
 851 
 852 /* run_referral() */
 853 /*
 854    invoked when no such domain found. Goes through the domain table
 855    and searches for shorter domains, then if it finds one with referral 
 856    it performs it, otherwise it just returns nothing.
 857 
 858    to perform referral, it actually composes the referral query 
 859    for a given host/port/type and calls the whois query function.
 860 
 861    Well, it returns nothing anyway (void). It just prints to the socket.
 862 
 863 */
 864 void run_referral(char *sourcename,
     /* [<][>][^][v][top][bottom][index][help] */
 865                   SQ_connection_t *sql_connection, 
 866                   Query_instructions *qis,   
 867                   Query_environ *qe, 
 868                   int qi_index,
 869                   acc_st *acc_credit) {
 870   char *dot = qis->qc->keys;
 871   char querystr[STR_L];
 872   SQ_row_t *row;
 873   SQ_result_set_t *result;
 874   char sql_command[STR_XL];
 875   int stop_loop=0;
 876   char *ref_host;
 877   char *ref_type;
 878   char *ref_port;
 879   int  ref_port_int;
 880 
 881   strcpy(querystr,"");
 882 
 883   while( !stop_loop && (dot=index(dot,'.')) != NULL ) {
 884     dot++;
 885 
 886     ER_dbg_va(FAC_QI, ASP_QI_REF_DET, "run_referral: checking %s", dot);
 887 
 888     sprintf(sql_command, "SELECT domain.object_id, domain, type, port, host FROM domain, refer WHERE domain.object_id = refer.object_id AND domain = '%s'", dot);
 889     dieif( SQ_execute_query(sql_connection, sql_command, &result) == -1);
 890 
 891     switch( SQ_num_rows(result) ) {
 892       case 0: /* no such domain -> no action, will try next chunk */
 893       break;
 894 
 895       case 1: /* check for referral host and perform query if present
 896                in any case end the loop */
 897       stop_loop=1;
 898       assert( (row = SQ_row_next(result)) != NULL);
 899       
 900       ref_host = SQ_get_column_string(result, row, 4);
 901 
 902       ER_dbg_va(FAC_QI, ASP_QI_REF_GEN, "referral host is %s", ref_host); 
 903 
 904       if( ref_host != NULL && strlen(ref_host) > 0 ) {
 905         ref_type = SQ_get_column_string(result, row, 2);
 906         ref_port = SQ_get_column_string(result, row, 3);
 907         
 908         /* get the integer value, it should be correct */
 909         if( sscanf( ref_port, "%d",&ref_port_int) < 1 ) {
 910           die;
 911         }
 912          
 913         /* compose the query: */
 914 
 915         /* put -r if the reftype is RIPE and -r or -i were used */
 916         if( strcmp(ref_type,"RIPE") == 0 
 917             && (   Query[qis->instruction[qi_index]->queryindex]
 918                    .querytype == Q_INVERSE       
 919                    || qis->recursive > 0  )   ) {
 920           strcat(querystr," -r ");
 921         }
 922 
 923         /* prepend with -Vversion,IP for type CLIENTADDRESS */
 924         if( strcmp(ref_type,"CLIENTADDRESS") == 0 ) {
 925           char optv[STR_M];
 926 
 927           snprintf(optv,STR_M," -V%s,%s ","RIP0.88", qe->condat.ip);
 928           strcat(querystr,optv);
 929         }
 930 
 931         /* now set the search term - set to the stripped down version 
 932            for inverse query, full-length otherwise */
 933         if( Query[qis->instruction[qi_index]->queryindex].querytype == Q_INVERSE ) {
 934           strcat(querystr,dot);
 935         }
 936         else {
 937           strcat(querystr,qis->qc->keys);
 938         }
 939         
 940         SK_cd_printf(&(qe->condat), 
 941 "%% The object shown below is NOT in the %s database.\n"
 942 "%% It has been obtained by querying a remote server:\n"
 943 "%% (%s) at port %d.\n"
 944 "%% To see the object stored in the %s database\n"
 945 "%% use the -R flag in your query\n"
 946 "%%\n"
 947 "%%%%%% Start of referred query result\n",
 948                      sourcename, 
 949                      ref_host, ref_port_int,
 950                      sourcename );
 951         SK_cd_puts(&(qe->condat), "\n");
 952 
 953         /* WH_sock(sock, host, port, query, maxlines, timeout)) */
 954         switch( WH_sock(qe->condat.sock, ref_host, ref_port_int, querystr,  25, 5) ) {
 955         case WH_TIMEOUT:
 956             SK_cd_puts(&(qe->condat),"referral timeout\n");
 957             break;
 958             
 959         case WH_MAXLINES:
 960             SK_cd_puts(&(qe->condat),"referral maxlines exceeded\n");
 961             break;
 962             
 963         case WH_BADHOST:
 964             SK_cd_puts(&(qe->condat),"referral host not found\n");
 965             break;
 966 
 967         case WH_CONNECT:
 968             SK_cd_puts(&(qe->condat),"referral host not responding\n");
 969             break;
 970 
 971         case WH_BIND:
 972         case WH_SOCKET:
 973             /* XXX internal server problem... what to do - wait ? */
 974         default:
 975           ;
 976         } /*switch WH_sock */
 977 
 978         SK_cd_puts(&(qe->condat), "\n%%% End of referred query result\n");
 979         acc_credit->referrals -= 1;
 980       }
 981       break;
 982 
 983       default: /* more than one domain in this file: something broken */
 984       die;
 985     }
 986     SQ_free_result(result);
 987   }
 988 } /*run_referral*/
 989 
 990 static
 991 void 
 992 add_ref_name(SQ_connection_t *sql_connection, 
     /* [<][>][^][v][top][bottom][index][help] */
 993              char *rectable,
 994              char *allnames
 995              )
 996 {
 997   /* construct the query, allow zero-length list */
 998   if( strlen(allnames) > 0 ) {
 999     char final_query[STR_XL];
1000     char select_query[STR_XL];
1001 
1002     create_name_query(select_query, "SELECT N00.object_id FROM %s WHERE %s "
1003                       "AND N00.object_type != 100 AND N00.thread_id = 0", 
1004                       allnames);
1005     
1006     sprintf(final_query, "INSERT INTO %s %s",
1007             rectable,
1008             select_query);
1009     
1010     dieif(SQ_execute_query(sql_connection, final_query, NULL) == -1 );
1011 
1012     allnames[0]=0;
1013   }
1014 }
1015 
1016 static
1017 void
1018 qi_collect_ids(ca_dbSource_t *dbhdl,
     /* [<][>][^][v][top][bottom][index][help] */
1019                char *sourcename,
1020                SQ_connection_t **sql_connection,
1021                Query_instructions *qis,
1022                Query_environ *qe,       
1023                char *id_table,
1024                GList **datlist,
1025                acc_st *acc_credit,
1026                acl_st *acl
1027                )
1028 {
1029   Query_instruction **ins=NULL;
1030   int i;
1031   int   count, errors=0;
1032   char sql_command[STR_XL];
1033   er_ret_t err;
1034   char sub_table[32];
1035   int limit ;
1036              /* a limit on the max number of objects to be returned
1037                 from a single search. For some queries the object types
1038                 are not known at this stage, so the limit must be
1039                 the higher number of the two: private / public,
1040                 or unlimited if any of them is 'unlimited'.
1041              */
1042   char limit_str[32];
1043 
1044   if( (limit = AC_get_higher_limit(acc_credit,acl)) == -1) {
1045     strcpy(limit_str,"");
1046   } else {
1047     sprintf(limit_str," LIMIT %d", limit+1); /* make sure we collect more
1048                                                 so that the client hits
1049                                                 the limit */
1050   }
1051 
1052   sprintf(sub_table, "%s_S ", id_table);
1053   
1054   /* see if there was a leftover table from a crashed session 
1055    * (assume the ID cannot be currently in use)
1056    */
1057   sprintf(sql_command, "DROP TABLE IF EXISTS %s", sub_table);
1058   dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1059 
1060   /* create a table for special subqueries (domain only for now) */
1061   sprintf(sql_command, "CREATE TABLE %s ( id int ) TYPE=HEAP", sub_table);
1062   dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1063   
1064   /* Iterate through query instructions */
1065   ins = qis->instruction;
1066   for (i=0; ins[i] != NULL && errors == 0; i++) {
1067     Query_instruction *qi = ins[i];
1068     
1069     /* check if the client is still there */
1070     if( qe->condat.rtc ) {
1071       break;
1072     }
1073 
1074     switch ( qi->search_type ) {
1075     case R_SQL:
1076       if ( qi->query_str != NULL ) {
1077 
1078         /* handle special cases first */
1079         if( Query[qi->queryindex].class == C_DN ) {
1080 
1081           /* XXX if any more cases than just domain appear, we will be
1082              cleaning the _S table from the previous query here 
1083              
1084              "DELETE FROM %s_S"
1085           */
1086 
1087           /* now query into the _S table */
1088           sprintf(sql_command, "INSERT INTO %s%s", sub_table, qi->query_str);
1089           dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1);
1090           
1091           /* if any results - copy to the id's table. 
1092              Otherwise, run referral */
1093           count = SQ_get_affected_rows(*sql_connection);
1094 
1095           ER_dbg_va(FAC_QI, ASP_QI_COLL_DET, 
1096                     "DN lookup for %s found %d entries", qis->qc->keys, count);
1097                  
1098           if( count ) {
1099             sprintf(sql_command, "INSERT INTO %s SELECT id FROM %s", 
1100                     id_table, sub_table);
1101             dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1);
1102           }
1103 
1104           if( count == 0 
1105               || Query[qi->queryindex].querytype == Q_INVERSE ) {
1106             /* now: if the domain was not found, we run referral.
1107                unless prohibited by a flag 
1108               
1109                But for inverse queries we return the things that were
1110                or were not found AND also do the referral unless prohibited.
1111             */
1112             if (qis->qc->R == 0) {
1113               run_referral(sourcename, *sql_connection, qis, qe, i, acc_credit);
1114             }
1115           }
1116           
1117         } /* if class DN */
1118         else {
1119           /* any other class of query */
1120 
1121           sprintf(sql_command, "INSERT INTO %s %s %s", 
1122                   id_table, qi->query_str, limit_str);
1123 
1124           if(sql_execute_watched( &(qe->condat), sql_connection, 
1125                                   sql_command, NULL) == -1 ) {
1126 
1127             ER_perror(FAC_QI, QI_SQLERR," query='%s' [%d] %s", 
1128                       sql_command,
1129                       SQ_errno(*sql_connection), SQ_error(*sql_connection));
1130             errors++;
1131           }
1132           count = SQ_get_affected_rows(*sql_connection);
1133         } /* not DN */
1134       } /* if SQL query not NULL */
1135       
1136       ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1137                 "%d entries added in %s query for %s",
1138                 count, Query[qi->queryindex].descr, qis->qc->keys
1139                 );
1140       break;
1141       
1142     case R_RADIX:
1143 
1144       if( ! qis->qc->S ) /* XXX patch: use new search algorithm by default */ {
1145         err = RP_new_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0, 
1146                             qi->rx_keys, dbhdl,
1147                             Query[qi->queryindex].attribute, 
1148                             datlist, limit);
1149 
1150       }
1151       else {
1152         err = RP_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0, 
1153                             qi->rx_keys, dbhdl, 
1154                             Query[qi->queryindex].attribute, 
1155                             datlist, limit);
1156       }
1157 
1158       if( NOERR(err)) {
1159         if( ER_is_traced(FAC_QI, ASP_QI_COLL_DET ) ) {
1160           /* prevent unnecessary g_list_length call */
1161           
1162           ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1163                     "%d entries after %s (mode %d par %d reg %d) query for %s",
1164                     g_list_length(*datlist),
1165                     Query[qi->queryindex].descr,
1166                     qi->rx_srch_mode, qi->rx_par_a, 
1167                     dbhdl,
1168                     qi->rx_keys);
1169         }
1170       }
1171       else {
1172         ER_inf_va(FAC_QI, ASP_QI_COLL_DET,
1173                   "RP_asc_search returned %x ", err);
1174       }
1175       break;
1176       
1177     default: die;
1178     } /* switch */
1179     
1180   } /* for <every instruction> */
1181 
1182   /* Now drop the _S table */
1183   sprintf(sql_command, "DROP TABLE %s", sub_table);
1184   dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1185 
1186 }
1187 
1188 static
1189 void
1190 qi_fetch_references(SQ_connection_t **sql_connection,
     /* [<][>][^][v][top][bottom][index][help] */
1191                     Query_environ *qe,
1192                     char *id_table,
1193                     acc_st *acc_credit,
1194                     acl_st *acl
1195                     )
1196 {
1197 char rec_table[32];
1198     SQ_result_set_t *result = NULL;
1199     SQ_row_t *row;
1200     int thisid = 0;
1201     int oldid = 0;
1202     char allnames[STR_M];
1203     char sql_command[STR_XL];
1204  
1205     sprintf(rec_table, "%s_R", id_table);
1206     
1207     /* see if there was a leftover table from a crashed session 
1208      * (assume the ID cannot be currently in use)
1209      */
1210     sprintf(sql_command, "DROP TABLE IF EXISTS %s ", rec_table);
1211     dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1212 
1213     /* a temporary table for recursive data must be created, because
1214        a query using the same table as a source and target is illegal
1215        ( like: INSERT into ID_123 SELECT * FROM ID_123,admin_c WHERE ... )
1216     */
1217     sprintf(sql_command, "CREATE TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", rec_table);
1218     dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1219     
1220     /* find the contacts */      
1221     sprintf(sql_command, Q_REC, rec_table, id_table, "author");
1222     dieif(sql_execute_watched( &(qe->condat), sql_connection, sql_command, NULL) == -1 );
1223     
1224     sprintf(sql_command, Q_REC, rec_table, id_table, "admin_c");
1225     dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, NULL) == -1 );
1226     
1227     sprintf(sql_command, Q_REC, rec_table, id_table, "tech_c" );
1228     dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, NULL) == -1 );
1229     
1230     sprintf(sql_command, Q_REC, rec_table, id_table, "zone_c" );
1231     dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, NULL) == -1 );
1232     
1233     
1234     /* replace references to dummies by references by name */
1235     sprintf(sql_command, 
1236             " SELECT id, name    FROM %s IDS STRAIGHT_JOIN names "
1237             " WHERE IDS.id = names.object_id "
1238             "      AND names.object_type = 100"
1239             " ORDER BY id",
1240             rec_table);
1241     
1242     dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, 
1243                               &result) == -1 );
1244     /* well, it might not be -1, but if the watchdog worked then the
1245        result is NULL */
1246     if( result != NULL ) {
1247       
1248       allnames[0]=0;
1249       /* now go through the results and collect names */
1250       while ( (qe->condat.rtc == 0)
1251               && (row = SQ_row_next(result)) != NULL ) {
1252         char *id   = SQ_get_column_string(result, row, 0);
1253         char *name = SQ_get_column_string(result, row, 1);
1254         
1255         thisid = atoi(id);
1256         
1257         /* when the id changes, the name is complete */
1258         if( thisid != oldid && oldid != 0 ) {
1259           add_ref_name( *sql_connection, rec_table, allnames);
1260         }
1261         
1262         strcat(allnames, name);
1263         strcat(allnames, " ");
1264         oldid = thisid;
1265         wr_free(id);
1266         wr_free(name);
1267       }
1268       /* also do the last name */
1269       add_ref_name( *sql_connection, rec_table, allnames);
1270       
1271       SQ_free_result(result); /* we can do it only because the watchdog */
1272       /* has not started between the check for non-NULL result and here */
1273     }
1274     
1275     /* now copy things back to the main temporary table   */
1276     sprintf(sql_command, "INSERT INTO %s SELECT * FROM %s", 
1277             id_table, rec_table);
1278     dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1279     
1280     /* Now drop the IDS recursive table */
1281     sprintf(sql_command, "DROP TABLE %s", rec_table);
1282     dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1283 }
1284 
1285 
1286 /* QI_execute() */
1287 /*++++++++++++++++++++++++++++++++++++++
1288   Execute the query instructions.  This is called for each source.
1289 
1290   void *database_voidptr Pointer to the database name
1291   
1292   void *qis_voidptr Pointer to the query_instructions.
1293    
1294   More:
1295   +html+ <PRE>
1296   Authors:
1297         ottrey
1298   +html+ </PRE>
1299   ++++++++++++++++++++++++++++++++++++++*/
1300 er_ret_t QI_execute(ca_dbSource_t *dbhdl,
     /* [<][>][^][v][top][bottom][index][help] */
1301                     Query_instructions *qis, 
1302                     Query_environ *qe,  
1303                     acc_st *acc_credit,
1304                     acl_st *acl
1305                     ) 
1306 {
1307   /* those things must be freed after use! */
1308   char *dbhost = ca_get_srcdbmachine(dbhdl);
1309   char *dbname = ca_get_srcdbname(dbhdl);
1310   char *dbuser = ca_get_srcdbuser(dbhdl);
1311   char *dbpass = ca_get_srcdbpassword(dbhdl);
1312   char *srcnam = ca_get_srcname(dbhdl);
1313   char id_table[STR_S];
1314   char sql_command[STR_XL];
1315   GList *datlist=NULL;
1316   SQ_connection_t *sql_connection=NULL;
1317 
1318   sql_connection = SQ_get_connection( dbhost, ca_get_srcdbport(dbhdl),
1319                                       dbname, dbuser, dbpass );
1320   if (sql_connection == NULL) {
1321     ER_perror(FAC_QI, QI_CANTDB," database='%s' [%d] %s", 
1322               dbname, SQ_errno(sql_connection), SQ_error(sql_connection));
1323     return QI_CANTDB;
1324   }
1325 
1326   sprintf(id_table, "ID_%ld_%d",   mysql_thread_id(sql_connection),
1327           pthread_self());
1328 
1329   /* see if there was a leftover table from a crashed session 
1330    * (assume the ID cannot be currently in use)
1331    */
1332   sprintf(sql_command, "DROP TABLE IF EXISTS %s ", id_table);
1333   dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
1334   
1335   /* create a table for id's of all objects found NOT NULL , UNIQUE(id) */
1336   sprintf(sql_command, "CREATE TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", id_table);
1337   dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
1338 
1339   qi_collect_ids(dbhdl, srcnam, &sql_connection, qis, qe, id_table, 
1340                  &datlist, acc_credit, acl);
1341 
1342   /* post-processing */
1343   if( qis->filtered == 0 ) {
1344     /* start the watchdog just to set the rtc flag */
1345     SK_watchclear(&(qe->condat));
1346     SK_watchstart(&(qe->condat));
1347 
1348     /* add radix results (only if -K is not active) */
1349     insert_radix_serials(&(qe->condat), sql_connection, id_table, datlist);
1350 
1351     SK_watchstop(&(qe->condat));
1352   }
1353 
1354   /* fetch recursive objects (ac,tc,zc,ah) */
1355   if ( qis->recursive ) {
1356     qi_fetch_references( &sql_connection, qe, id_table, acc_credit, acl);
1357   } /* if recursive */
1358   
1359   /* display */
1360   /* -K filtering: 
1361    * right now only filtering, no expanding sets like write_set_objects() 
1362    */
1363   
1364   /* display the immediate data from the radix tree */
1365   if( qis->filtered == 1 ) {
1366     write_radix_immediate(datlist, &(qe->condat), acc_credit, acl );
1367   }
1368 
1369   /* display objects from the IDs table */
1370   write_objects( &sql_connection, id_table, qis->filtered,
1371                 qis->fast, &(qe->condat), acc_credit, acl);
1372 
1373   /* Now drop the IDS table */
1374   sprintf(sql_command, "DROP TABLE %s", id_table);
1375   dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
1376   SQ_close_connection(sql_connection);  
1377 
1378   /* free allocated parameters */
1379   wr_free(dbhost);
1380   wr_free(dbname);
1381   wr_free(dbuser);
1382   wr_free(dbpass);
1383   wr_free(srcnam);
1384 
1385   return QI_OK;
1386 } /* QI_execute() */
1387 
1388 
1389 /* instruction_free() */
1390 /*++++++++++++++++++++++++++++++++++++++
1391   Free the instruction.
1392 
1393   Query_instruction *qi query_instruction to be freed.
1394    
1395   More:
1396   +html+ <PRE>
1397   Authors:
1398         ottrey
1399   +html+ </PRE>
1400   ++++++++++++++++++++++++++++++++++++++*/
1401 static void instruction_free(Query_instruction *qi) {
     /* [<][>][^][v][top][bottom][index][help] */
1402   if (qi != NULL) {
1403     if (qi->query_str != NULL) {
1404       wr_free(qi->query_str);
1405     }
1406     wr_free(qi);
1407   }
1408 } /* instruction_free() */
1409 
1410 /* QI_free() */
1411 /*++++++++++++++++++++++++++++++++++++++
1412   Free the query_instructions.
1413 
1414   Query_instructions *qis Query_instructions to be freed.
1415    
1416   XXX This isn't working too well at the moment.
1417 
1418   More:
1419   +html+ <PRE>
1420   Authors:
1421         ottrey
1422   +html+ </PRE>
1423   ++++++++++++++++++++++++++++++++++++++*/
1424 void QI_free(Query_instructions *qis) {
     /* [<][>][^][v][top][bottom][index][help] */
1425   int i;
1426 
1427   for (i=0; qis->instruction[i] != NULL; i++) {
1428     instruction_free(qis->instruction[i]);
1429   } 
1430 
1431   if (qis != NULL) {
1432     wr_free(qis);
1433   }
1434 
1435 } /* QI_free() */
1436 
1437 /*++++++++++++++++++++++++++++++++++++++
1438   Determine if this query should be conducted or not.
1439 
1440   If it was an inverse query - it the attribute appears in the query command's bitmap.
1441   If it was a lookup query - if the attribute appears in the object type bitmap or
1442                              disregard if there is no object_type bitmap (Ie object filter).
1443 
1444   mask_t bitmap The bitmap of attribute to be converted.
1445    
1446   const Query_command *qc The query_command that the instructions are created
1447                           from.
1448   
1449   const Query_t q The query being investigated.
1450 
1451   ++++++++++++++++++++++++++++++++++++++*/
1452 static int valid_query(const Query_command *qc, const Query_t q) {
     /* [<][>][^][v][top][bottom][index][help] */
1453   int result=0;
1454 
1455   if (MA_isset(qc->keytypes_bitmap, q.keytype) == 1) {
1456     if (q.query != NULL) {
1457       switch (q.querytype) {
1458         case Q_INVERSE:
1459           if (MA_isset(qc->inv_attrs_bitmap, q.attribute) ) {
1460             result = 1;
1461           }
1462         break;
1463 
1464         case Q_LOOKUP:
1465           if (MA_bitcount(qc->object_type_bitmap) == 0) {
1466             result=1;
1467           }
1468           else if (q.class<0 || MA_isset(qc->object_type_bitmap, q.class)) {
1469             result=1;
1470           }
1471         break;
1472 
1473         default:
1474           fprintf(stderr, "qi:valid_query() -> Bad querytype\n");
1475       }
1476     }
1477   }
1478 
1479   return result;
1480 } /* valid_query() */
1481 
1482 /* QI_new() */
1483 /*++++++++++++++++++++++++++++++++++++++
1484   Create a new set of query_instructions.
1485 
1486   const Query_command *qc The query_command that the instructions are created
1487                           from.
1488 
1489   const Query_environ *qe The environmental variables that they query is being
1490                           performed under.
1491   More:
1492   +html+ <PRE>
1493   Authors:
1494         ottrey
1495   +html+ </PRE>
1496   ++++++++++++++++++++++++++++++++++++++*/
1497 Query_instructions *QI_new(const Query_command *qc, const Query_environ *qe) {
     /* [<][>][^][v][top][bottom][index][help] */
1498   Query_instructions *qis=NULL;
1499   Query_instruction *qi=NULL;
1500   int i_no=0;
1501   int i;
1502   char *query_str;
1503 
1504   dieif(wr_calloc( (void **) & qis, 1, sizeof(Query_instructions)) != UT_OK);
1505 
1506   qis->filtered = qc->filtered;
1507   qis->fast = qc->fast;
1508   qis->recursive = qc->recursive;
1509   qis->qc = (qc);
1510 
1511   
1512   for (i=0; Query[i].query != NULL; i++) {
1513 
1514     /* If a valid query. */
1515     if ( valid_query(qc, Query[i]) == 1) {
1516 
1517       dieif( wr_calloc((void **) &qi, 1, sizeof(Query_instruction)) != UT_OK);
1518 
1519       qi->queryindex = i;
1520 
1521       /* SQL Query */
1522       if ( Query[i].refer == R_SQL) {
1523         qi->search_type = R_SQL;
1524         query_str = create_query(Query[i], qc);
1525 
1526         if (query_str!= NULL) {
1527           qi->query_str = query_str;
1528           qis->instruction[i_no++] = qi;
1529         }
1530       }
1531       /* Radix Query */
1532       else if (Query[i].refer == R_RADIX) {
1533         qi->search_type = R_RADIX;
1534         
1535         if (map_qc2rx(qi, qc) == 1) {
1536           int j;
1537           int found=0;
1538           
1539           /* check that there is no such query yet, for example if
1540              more than one keytype (wk) matched */
1541           for (j=0; j<i_no; j++) {
1542             Query_instruction *qij = qis->instruction[j];
1543             
1544             if(    qij->search_type == R_RADIX
1545                    && Query[qij->queryindex].attribute 
1546                    == Query[qi ->queryindex].attribute) {
1547               
1548               found=1;
1549               break;
1550             }
1551           }
1552           
1553           if ( found ) {
1554             /* Discard the Query Instruction */
1555             wr_free(qi);
1556           } 
1557           else {
1558             /* Add the query_instruction to the array */
1559             qis->instruction[i_no++] = qi;
1560           }
1561         }
1562       }
1563       else {
1564           /* ERROR: bad search_type */
1565           die;
1566       }
1567     }
1568   }
1569   qis->instruction[i_no++] = NULL;
1570 
1571 
1572   {  /* tracing */
1573       char *descrstr = QI_queries_to_string(qis);
1574 
1575       ER_dbg_va(FAC_QI, ASP_QI_COLL_GEN, "Queries: %s", descrstr );
1576       wr_free( descrstr );
1577   }
1578 
1579   return qis;
1580 
1581 } /* QI_new() */
1582 
1583 /* QI_queries_to_string() 
1584    
1585    returns a list of descriptions for queries that will be performed.
1586 */
1587 
1588 char *QI_queries_to_string(Query_instructions *qis)
     /* [<][>][^][v][top][bottom][index][help] */
1589 {
1590    Query_instruction *qi;
1591    int i;
1592    char *resstr = NULL;
1593 
1594    dieif( wr_realloc((void **)&resstr, 2 ) != UT_OK);
1595    strcpy(resstr, "{");
1596 
1597    for( i = 0; ( qi=qis->instruction[i] ) != NULL;  i++ ) {
1598        char *descr = Query[qi->queryindex].descr;
1599        int oldres = strlen( resstr );
1600        
1601        dieif( wr_realloc((void **)&resstr, oldres+strlen(descr)+2) != UT_OK);
1602        strcat(resstr, descr);
1603        strcat(resstr, ",");
1604    }
1605    if( i>0 ) {
1606        /* cancel the last comma */
1607        resstr[strlen(resstr)-1] = 0;
1608    }
1609 
1610    dieif( wr_realloc((void **)&resstr, strlen( resstr ) + 2 ) 
1611           != UT_OK);
1612    strcat(resstr, "}");
1613    
1614    return resstr;
1615 }

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