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. QI_fast_output
  8. filter
  9. write_results
  10. write_objects
  11. insert_radix_serials
  12. write_radix_immediate
  13. map_qc2rx
  14. run_referral
  15. qi_prep_run_refer
  16. qi_collect_domain
  17. add_ref_name
  18. qi_collect_ids
  19. qi_fetch_references
  20. QI_execute
  21. instruction_free
  22. QI_free
  23. valid_query
  24. QI_new
  25. QI_queries_to_string

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

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