modules/qi/query_instructions.c

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

FUNCTIONS

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

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

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