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

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