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

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