modules/qi/query_instructions.c

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

DEFINITIONS

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. __report_sql_error
  11. write_objects
  12. rx_node_has_mnt_irt
  13. mnt_irt_filter
  14. insert_radix_serials
  15. write_radix_immediate
  16. map_qc2rx
  17. run_referral
  18. qi_prep_run_refer
  19. qi_collect_domain
  20. add_ref_name
  21. qi_collect_ids
  22. qi_fetch_references
  23. QI_execute
  24. instruction_free
  25. QI_free
  26. valid_query
  27. QI_new
  28. QI_queries_to_string

   1 /***************************************
   2   $Revision: 1.96 $
   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,2000,2001,2002               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 "rip.h"
  35 
  36 #include <stdio.h>
  37 #include <string.h>
  38 #include <glib.h>
  39 
  40 /* maximum number of names to query for */
  41 /*   MySQL only allows 31 tables in a single query, so this limits us */
  42 /*   As of 2002-03-04 the most names in a person object are 14 */
  43 #define MAX_NAMES 30
  44 
  45 /* the TEMPORARY table is only available in 3.23 or later MySQL */
  46 #if MYSQL_VERSION_ID >= 32300
  47 #define USE_TEMPORARY_TABLE 
  48 #endif
  49 
  50 /* string to stick in CREATE TABLE statments */
  51 #ifdef USE_TEMPORARY_TABLE
  52 #define TEMPORARY "TEMPORARY"
  53 #else
  54 #define TEMPORARY ""
  55 #endif
  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(GString *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   if (words[0] != NULL) {
 213     g_string_sprintfa(from_clause, "names N%.2d", 0);
 214     g_string_sprintfa(where_clause, "N%.2d.name=\"%s\"", 0, words[0]);
 215 
 216     for (i=1; (i < MAX_NAMES) && (words[i] != NULL); i++) {
 217       g_string_sprintfa(from_clause, ", names N%.2d", i);
 218       g_string_sprintfa(where_clause, 
 219           " AND N%.2d.name=\"%s\" AND N00.object_id = N%.2d.object_id", 
 220           i, words[i], i);
 221     }
 222   }
 223 
 224   g_string_sprintf(query_str, sql_query, from_clause->str, where_clause->str);
 225 
 226   /* Free up stuff */
 227   g_strfreev(words);
 228   g_string_free(where_clause,/* CONSTCOND */ TRUE);
 229   g_string_free(from_clause, /* CONSTCOND */ TRUE);
 230 
 231 } /* create_name_query() */
 232 
 233 
 234 /*++++++++++++++++++++++++++++++++++++++
 235   construct a range query for the as_block table
 236   (a query for an AS block object) given a string like: 
 237   AS1
 238   AS1 - AS10
 239   AS1-AS10
 240 
 241   int create_asblock_query    Returns 0 on success, -1 on failure 
 242                              (search term not an AS# nor range)
 243 
 244   char *query_str             buffer for the final query (must be big enough)
 245 
 246   const char *sql_query       rest of the sql query (with %d %d formats for
 247                               AS numbers)
 248 
 249   const char *keys            user-supplied search term.
 250 
 251   Author:
 252     marek
 253   ++++++++++++++++++++++++++++++++++++++*/
 254 static int create_asblock_query(GString *query_str, 
     /* [<][>][^][v][top][bottom][index][help] */
 255                                 const char *sql_query, 
 256                                 const char *keys) {
 257   char *keycopy = wr_string(keys);
 258   char *token, *cursor = keycopy;
 259   int  asnums[2] = {0,0};
 260   int index = 0; /* index into the asnums array */
 261 
 262 
 263   while( (token = strsep( &cursor, "-" )) != NULL && index < 2) {  
 264     /* discard the letters (or leading whitespace), take the number */
 265     if( sscanf(token, "%*[ AS]%d", &asnums[index++]) < 1 ) {
 266       return -1; /* error */
 267     }
 268   }
 269   /* if only beginning was supplied, copy it as end */
 270   if( index == 1 ) {
 271     asnums[1] = asnums[0];
 272   }
 273   
 274   /* now construct the query */
 275   g_string_sprintf(query_str, sql_query, asnums[0], asnums[1]);
 276 
 277   UT_free(keycopy);
 278   return 0;
 279 }
 280 
 281 
 282 /*++++++++++++++++++++++++++++++++++++++
 283   add_filter(): construct a query to limit the objects returned from the last
 284   table to predefined types. 
 285 
 286   char *query_str           buffer for the final query, containing the initial
 287                             part of the query (must be big enough)
 288 
 289   const Query_command *qc   query command structure with the bitmap of 
 290                             object types to be included.
 291                             
 292   Author:
 293     ottrey.
 294   ++++++++++++++++++++++++++++++++++++++*/
 295 static void add_filter(GString *query_str, const Query_command *qc) 
     /* [<][>][^][v][top][bottom][index][help] */
 296 {
 297   unsigned i;
 298 /*  int qlen;*/
 299   char filter_atom[STR_M];
 300 
 301 #if 0
 302   /* glib string manipulation - untested yet */
 303   
 304     if (MA_bitcount(qc->object_type_bitmap) != MASK_MAX) { 
 305       g_string_sprintfa(query_str, " AND (");
 306       for (i=0; i < C_END; i++) {
 307         if (MA_isset(qc->object_type_bitmap, i)) {
 308           g_string_sprintfa(query_str, "i.object_type = %d OR ", DF_get_class_dbase_code(i));
 309         }
 310       }
 311       g_string_truncate(query_str, query_str->len-3);
 312       g_string_append_c(query_str, ')');
 313     }
 314 
 315 #else /* classic string operations */
 316 
 317   /* add filters only if any bits are 0 (the number of 1's is < MAX_MAX */
 318   if (MA_bitcount(qc->object_type_bitmap) != MASK_MAX) { 
 319     g_string_append(query_str, " AND (");
 320     for (i=0; i < C_END; i++) {
 321       if (MA_isset(qc->object_type_bitmap, i)) {
 322         strcpy(filter_atom, "");
 323         sprintf(filter_atom, "i.object_type = %d OR ", i);
 324                         /* XXX class codes should be used instead:
 325                            DF_get_class_dbase_code(i)) 
 326                            but currently the tables contain values of enums
 327                            (C_IN, etc) and not codes
 328                         */
 329         g_string_append(query_str, filter_atom);
 330       }
 331     }
 332     dieif(query_str->len < 3);  /* this code can only be reached if there is
 333                                    at least one object here, meaning this 
 334                                    must end with the string "OR ", which we
 335                                    then remove */
 336     g_string_truncate(query_str, query_str->len - 3);
 337     g_string_append_c(query_str, ')');
 338 /*    qlen = strlen(query_str);
 339     query_str[qlen-3] = ')';
 340     query_str[qlen-2] = '\0';
 341     query_str[qlen-1] = '\0';*/
 342   }
 343 
 344 #endif
 345   
 346 } /* add_filter() */
 347 
 348 /* create_query() */
 349 /*++++++++++++++++++++++++++++++++++++++
 350   Create an sql query from the query_command and the matching keytype and the
 351   selected inverse attributes.
 352   Note this clears the first inv_attribute it sees, so is called sequentially
 353   until there are no inv_attributes left.
 354 
 355   WK_Type keytype The matching keytype.
 356 
 357   const Query_command *qc The query command.
 358 
 359   mask_t *inv_attrs_bitmap The selected inverse attributes.
 360    
 361   More:
 362   +html+ <PRE>
 363   Authors:
 364         ottrey
 365   +html+ </PRE>
 366 
 367   ++++++++++++++++++++++++++++++++++++++*/
 368 static char *create_query(const Query_t q, const Query_command *qc) 
     /* [<][>][^][v][top][bottom][index][help] */
 369 {
 370   GString *result_buff;
 371   char *result;
 372   Q_Type_t querytype;
 373   int addquery = 0; /* controls if the query should be added to the list */
 374 
 375   result_buff = g_string_sized_new(STR_XL);
 376 
 377   if (MA_bitcount(qc->inv_attrs_bitmap) > 0) {
 378     querytype = Q_INVERSE;
 379   }
 380   else {
 381     querytype = Q_LOOKUP;
 382   }
 383 
 384   if ( (q.query != NULL) 
 385     && (q.querytype == querytype) ) {
 386     
 387     /* addquery = 1; */
 388     /* if it got here, it should be added, unless.(see asblock)*/
 389     
 390     if (q.keytype == WK_NAME) { 
 391       /* Name queries require special treatment. */
 392       create_name_query(result_buff, q.query, qc->keys);
 393       addquery = 1;
 394     }
 395     else if( q.keytype == WK_IPADDRESS ) {  /* ifaddr sql lookups */
 396       ip_range_t myrang;
 397       unsigned   begin, end;
 398       ip_keytype_t key_type;
 399       
 400       /* The only inverse query for IPADDRESS is nserver. */
 401       /* We need to insure that we don't try to use the numeric values for this
 402        * query, because the address of the server is stored as a string, and
 403        * the SQL query is formatted appropriately. */
 404       if (NOERR(IP_smart_range(qc->keys, &myrang, IP_EXPN, &key_type))) {
 405         if(IP_rang_b2_space(&myrang) == IP_V4 ) {
 406           IP_rang_b2v4(&myrang, &begin, &end);
 407           if (querytype == Q_INVERSE) {
 408               /* for inverse queries, convert number to dotted-quad */
 409               char buf[64];
 410               const char *inet_ntop_ret;
 411               inet_ntop_ret = inet_ntop(AF_INET, &begin, buf, sizeof(buf));
 412               dieif(inet_ntop_ret == NULL);
 413               g_string_sprintf(result_buff, q.query, buf);
 414           } else {
 415               /* otherwise, execute appropriate query on numeric values */
 416               g_string_sprintf(result_buff, q.query, begin, end);
 417           }
 418           addquery = 1;
 419         }
 420         else {
 421           die;
 422         }
 423       }
 424     }
 425     else if( q.keytype == WK_ASRANGE ) {   /* as_block range composition */
 426       if( create_asblock_query(result_buff, q.query, qc->keys) != 0 ) {
 427         addquery = 0; /* ... unless it's not correct */
 428       }
 429       else {
 430         addquery = 1;
 431       }
 432     }
 433     else {
 434       g_string_sprintf(result_buff, q.query, qc->keys);
 435       addquery = 1;
 436     }
 437 
 438     if (q.class == C_ANY && addquery == 1 ) {
 439       /* It is class type ANY so add the object filtering */
 440       add_filter(result_buff, qc);
 441     }
 442   }
 443   
 444   if( addquery == 1 ) {
 445     result = UT_strdup(result_buff->str);
 446   } else {
 447     result = NULL;
 448   }
 449   g_string_free(result_buff, TRUE);
 450 
 451   return result;
 452 } /* create_query() */
 453 
 454 /* QI_fast_output() */
 455 /*++++++++++++++++++++++++++++++++++++++
 456   This is for the '-F' flag.
 457   It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
 458   Fast isn't fast anymore - it's just there for compatibility reasons.
 459 
 460   const char *str        The object to be "fast output'ed".
 461    
 462   More:
 463   +html+ <PRE>
 464   Authors:
 465         ottrey,
 466         marek - glib strings + small changes
 467   +html+ </PRE>
 468   ++++++++++++++++++++++++++++++++++++++*/
 469 char *QI_fast_output(const char *str) 
     /* [<][>][^][v][top][bottom][index][help] */
 470 {
 471   int i,j;
 472   char *result;
 473   GString *result_buff = g_string_sized_new(STR_XL);
 474   gchar **lines = g_strsplit(str, "\n", 0);
 475   unsigned char *value, *colon;
 476   char *attr;
 477 
 478   g_string_assign(result_buff, "");
 479   
 480   for (j=0; lines[j] != NULL; j++) {
 481 
 482     switch (lines[j][0]) {
 483       /* line continuation */
 484     case ' ':
 485     case '\t':
 486     case '+':
 487       value = (unsigned char *) lines[j]+1;
 488       while(*value != '\0' && isspace(*value)) {
 489         value++;
 490       }      
 491       g_string_append(result_buff, "\n+ ");
 492       g_string_append(result_buff, (char *)value);
 493       break;
 494       
 495     default:
 496       /* a line of the form "attribute: value" */
 497       /* first: close the last line (if there was any, i.e. j>0) */
 498       if( j > 0 ) { 
 499         g_string_append_c(result_buff, '\n');
 500       }
 501       
 502       /* get attribute name */
 503       attr =  lines[j];
 504       colon = (unsigned char *) strchr(lines[j], ':');
 505       /* if there's no colon for whatever reason, dump the object
 506          and report the condition */
 507       if( colon == NULL ) {
 508         ER_perror(FAC_QI, QI_INVOBJ, " [%s]", lines[0]);
 509         goto fast_output_cleanup;
 510       }
 511       *colon = '\0';
 512       for(value = colon+1; *value != '\0' && isspace(*value) ; value++) {
 513         ;
 514       }
 515 
 516       if( (i = DF_attribute_name2type(attr)) == -1 ) {
 517           /* warning! error in the object format */
 518         ER_perror(FAC_QI, QI_INVOBJ, " [%s]", lines[0]);
 519         goto fast_output_cleanup;
 520                 
 521       }
 522       else {
 523         /* This is the juicy bit that converts the likes of; "source: RIPE" to "*so: RIPE" */
 524         g_string_append_c(result_buff, '*');
 525         g_string_append(result_buff, DF_get_attribute_code(i));
 526         g_string_append(result_buff, ": ");
 527         g_string_append(result_buff, (char *)value);
 528       }
 529     } /* switch */  
 530   } /* for every line */ 
 531 
 532  fast_output_cleanup:
 533 
 534   g_strfreev(lines);
 535   
 536   g_string_append_c(result_buff, '\n');
 537   result = UT_strdup(result_buff->str);
 538   dieif(result == NULL);
 539   
 540   g_string_free(result_buff,/* CONSTCOND */ TRUE);
 541   
 542   return result;
 543 } /* fast_output() */
 544 
 545 /* filter() */
 546 /*++++++++++++++++++++++++++++++++++++++
 547   Basically it's for the '-K' flag for non-set (and non-radix) objects.
 548   It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
 549 
 550   This could be speed up if there were breaks out of the loops, once it matched something.
 551 
 552   const char *string          The string to be filtered.
 553    
 554   More:
 555   +html+ <PRE>
 556   Authors:
 557         ottrey
 558   +html+ </PRE>
 559 
 560   ++++++++++++++++++++++++++++++++++++++*/
 561 char *filter(const char *str) {
     /* [<][>][^][v][top][bottom][index][help] */
 562   int i,j, passed=0;
 563   char *result;
 564   GString *result_buff = g_string_sized_new(STR_XL);
 565   gchar **lines = g_strsplit(str, "\n", 0);
 566   char * const *filter_names;
 567   gboolean filtering_an_attribute = FALSE;
 568   
 569   filter_names = DF_get_filter_names();
 570 
 571   g_string_assign(result_buff, "");
 572   
 573   for (i=0; filter_names[i] != NULL; i++) {
 574     for (j=0; lines[j] != NULL; j++) {
 575       /* match lines that start with "key-field:" */
 576       int filter_name_len = strlen(filter_names[i]);
 577       if ((strncmp(filter_names[i], lines[j], filter_name_len) == 0) &&
 578           (lines[j][filter_name_len] == ':'))
 579       {
 580 
 581         g_string_sprintfa(result_buff, "%s\n", lines[j]);
 582         passed++;
 583         
 584         /* CONSTCOND */
 585         filtering_an_attribute = TRUE;
 586       }
 587       /* CONSTCOND */
 588       else if (filtering_an_attribute == TRUE) {
 589         switch (lines[j][0]) {
 590           case ' ':
 591           case '\t':
 592           case '+':
 593 
 594             g_string_sprintfa(result_buff, "%s\n", lines[j]);
 595             
 596           break;
 597 
 598           default:
 599             filtering_an_attribute = FALSE;
 600         }
 601       }
 602     }
 603   }
 604 
 605   g_strfreev(lines);
 606 
 607   if(passed) {
 608     g_string_append(result_buff, "\n");
 609   }
 610   result = UT_strdup(result_buff->str);
 611   g_string_free(result_buff,/* CONSTCOND */ TRUE);
 612 
 613   return result;
 614 } /* filter() */
 615 
 616 /* write_results() */
 617 /*++++++++++++++++++++++++++++++++++++++
 618   Write the results to the client socket.
 619 
 620   SQ_result_set_t *result The result set returned from the sql query.
 621   unsigned filtered       if the objects should go through a filter (-K)
 622   sk_conn_st *condat      Connection data for the client    
 623 
 624   More:
 625   +html+ <PRE>
 626   Authors:
 627         ottrey - initial design
 628         marek - rewritten for accounting and cancellation.
 629   +html+ </PRE>
 630 
 631   ++++++++++++++++++++++++++++++++++++++*/
 632 static int write_results(SQ_result_set_t *result, 
     /* [<][>][^][v][top][bottom][index][help] */
 633                          unsigned filtered,
 634                          unsigned fast,
 635                          sk_conn_st *condat,
 636                          acc_st    *acc_credit,
 637                          acl_st    *acl
 638                          ) {
 639   SQ_row_t *row;
 640   char *str;
 641   char *filtrate;
 642   char *fasted;
 643   int retrieved_objects=0;
 644   char *objt;
 645   int type;
 646 
 647   /* Get all the results - one at a time */
 648   if (result != NULL) {
 649     /* here we are making use of the mysql_store_result capability
 650        of interrupting the cycle of reading rows. mysql_use_result
 651        would not allow that, would have to be read until end */
 652     
 653     while ( condat->rtc == 0 
 654             && AC_credit_isdenied( acc_credit ) == 0
 655             && (row = SQ_row_next(result)) != NULL ) {
 656       
 657       dieif (  (str = SQ_get_column_string(result, row, 0)) == NULL )
 658       dieif (  (objt = SQ_get_column_string(result, row, 3)) == NULL );
 659 
 660       /* get + add object type */
 661       type = atoi(objt);
 662         
 663       /* ASP_QI_LAST_DET */
 664       ER_dbg_va(FAC_QI, ASP_QI_LAST_DET,
 665                 "Retrieved serial id = %d , type = %s", atoi(str), objt);
 666         
 667       UT_free(str);
 668       UT_free(objt);
 669       
 670       /* decrement credit for accounting purposes */
 671       AC_count_object( acc_credit, acl, 
 672                        type == C_PN || type == C_RO ); /* is private? */
 673 
 674       /* break the loop if the credit has just been exceeded and 
 675          further results denied */
 676       if( AC_credit_isdenied( acc_credit ) ) {
 677         continue; 
 678       }
 679       
 680       if ((str = SQ_get_column_string(result, row, 2)) == NULL) { die; } 
 681       else {
 682         
 683         /* The fast output stage */
 684         if (fast == 1) {
 685           fasted = QI_fast_output(str);
 686           UT_free(str);
 687           str = fasted;
 688         }
 689         
 690         /* The filtering stage */
 691         if (filtered == 0) {
 692           SK_cd_puts(condat, str);
 693           SK_cd_puts(condat, "\n");
 694         }
 695         else { 
 696           
 697           /* XXX accounting should be done AFTER filtering, not to count
 698              objects filtered out */
 699 
 700           filtrate = filter(str);
 701           SK_cd_puts(condat, filtrate);
 702           UT_free(filtrate);
 703         }
 704         retrieved_objects++;
 705       }
 706       UT_free(str);
 707     }
 708   }
 709   
 710   return retrieved_objects;
 711 } /* write_results() */
 712 
 713 /* generic SQL error message - it could be a configurable parameter, but
 714    it also shouldn't happen! */
 715 static const char *sql_error_text = 
 716     "% An internal database error has occurred.\n"  
 717     "% It has been logged, and an adminstrator should look at it shorly.\n"
 718     "% Please try your query again.\n"
 719     "\n"
 720     "\n";
 721 
 722 /* use a macro so we can get our file and line number */
 723 #define report_sql_error(condat,sql_connection,sql_command) \
 724   __report_sql_error((condat), (sql_connection), (sql_command), \
 725                      __FILE__,__LINE__,pthread_self())
 726 
 727 /* report_sql_error() */
 728 /*++++++++++++++++++++++++++++++++++++++
 729 
 730   sk_conn_st *condat                 connection with user
 731   SQ_connection_t *sql_connection    connection to MySQL
 732   const char *sql_command            SQL command sent to MySQL
 733 
 734   ++++++++++++++++++++++++++++++++++++++*/
 735 
 736 void
 737 __report_sql_error(sk_conn_st *condat, 
     /* [<][>][^][v][top][bottom][index][help] */
 738                  SQ_connection_t *sql_connection, 
 739                  const char *sql_command,
 740                  const char *file,
 741                  int line,
 742                  pthread_t tid)
 743 {
 744     /* first, let user know what has happened */
 745     SK_cd_puts(condat, sql_error_text);
 746 
 747     /* next, log this error */
 748     ER_perror(FAC_QI, QI_SQLERR, "at %s:%d, tid:%lu [%d] %s sql='%s'", 
 749                       file, line, (unsigned long)tid,
 750                       SQ_errno(sql_connection), 
 751                       SQ_error(sql_connection),
 752                       sql_command);
 753 }
 754 
 755 /* write_objects() */
 756 /*++++++++++++++++++++++++++++++++++++++
 757   
 758   SQ_connection_t *sql_connection The connection to the database.
 759 
 760   char *id_table The id of the temporary table (This is a result of the hacky
 761                   way we've tried to get MySQL to do sub-selects.)
 762 
 763   sk_conn_st *condat  Connection data for the client
 764 
 765   More:
 766   +html+ <PRE>
 767   Authors:
 768         ottrey,
 769         marek.
 770   +html+ </PRE>
 771   ++++++++++++++++++++++++++++++++++++++*/
 772 static int 
 773 write_objects(SQ_connection_t **sql_connection, 
     /* [<][>][^][v][top][bottom][index][help] */
 774                           char *id_table, 
 775                           unsigned int filtered, 
 776                           unsigned int fast, 
 777                           sk_conn_st *condat,
 778                           acc_st    *acc_credit,
 779                           acl_st    *acl
 780                           ) 
 781 {
 782   SQ_result_set_t *result = NULL;
 783   int retrieved_objects=0;
 784   char sql_command[STR_XL];  
 785 
 786   sprintf(sql_command, Q_OBJECTS, id_table);
 787 
 788   if (sql_execute_watched(condat, sql_connection, sql_command, &result) == -1) {
 789       report_sql_error(condat, *sql_connection, sql_command);
 790       return SQ_errno(*sql_connection);
 791   }
 792   
 793   /* Problem: if the query was aborted, the result structure does not
 794      refer to any existing connection anymore. So we check rtc here.
 795   */
 796   
 797   if( condat->rtc == 0) {
 798     retrieved_objects = write_results(result, filtered, fast, condat, 
 799                                       acc_credit, acl);
 800     SQ_free_result(result); 
 801   }
 802   return 0;
 803 } /* write_objects() */
 804 
 805 /* rx_node_has_mnt_irt() */
 806 /*++++++++++++++++++++++++++++++++++++++
 807   See if the node has an "mnt-irt:" attribute.
 808 
 809   condat           connection data for the client
 810   sql_connection   connection to the database
 811   rx_data          data node returned from the RX module
 812   ++++++++++++++++++++++++++++++++++++++*/
 813 gboolean
 814 rx_node_has_mnt_irt (sk_conn_st *condat, 
     /* [<][>][^][v][top][bottom][index][help] */
 815                      SQ_connection_t *sql_connection, 
 816                      int object_id)
 817 {
 818     GString *sql_command;
 819     SQ_result_set_t *result_ptr;
 820     int sql_error;
 821     SQ_row_t *row;
 822     long num_irt_ref;
 823 
 824     /* query database for references from this object to an IRT */
 825     sql_command = g_string_new("");
 826     g_string_sprintf(sql_command, 
 827                      "SELECT COUNT(*) FROM mnt_irt WHERE object_id=%d",
 828                      object_id);
 829     if (SQ_execute_query(sql_connection, sql_command->str, &result_ptr) == -1) {
 830         sql_error = SQ_errno(sql_connection);
 831         report_sql_error(condat, sql_connection, sql_command->str);
 832         g_string_free(sql_command, TRUE);
 833         return FALSE;
 834     }
 835     g_string_free(sql_command, TRUE);
 836 
 837     /* get number of references */
 838     row = SQ_row_next(result_ptr);
 839     if (row == NULL) {
 840         /* if the object is deleted after we have gotten a reference to it,
 841            the lookup will fail - in such a case treat it the same as 
 842            an object without any mnt_irt attributes */
 843         num_irt_ref = 0;
 844     } else {
 845         if (SQ_get_column_int(result_ptr, row, 0, &num_irt_ref) != 0) {
 846             ER_perror(FAC_QI, QI_SQLERR, "bad column at %s:%d", 
 847                       __FILE__, __LINE__);
 848             num_irt_ref = 0;
 849         }
 850     }
 851     SQ_free_result(result_ptr);
 852 
 853     /* return appropriate result */
 854     if (num_irt_ref > 0) {
 855         return TRUE;
 856     } else {
 857         return FALSE;
 858     }
 859 }
 860 
 861 
 862 /* mnt_irt_filter() */
 863 /*++++++++++++++++++++++++++++++++++++++
 864   Find the most specific entry in the list of network nodes passed
 865   and remove all entries but that one from the list.
 866 
 867   condat           connection data for the client
 868   sql_connection   connection to the database
 869   datlist          list of data from the radix tree (nodes of *rx_datcpy_t)
 870 
 871   Note:  The "-c" query flag specifies that the most specific inetnum or
 872          inet6num with an "mnt-irt:" attribute should be returned.  
 873          
 874          To do this, we get a list of encompassing networks by sending the 
 875          same options to the RP/RX module that the '-L' query does.  Then
 876          we call this function, mnt_irt_filter(), and search the list for 
 877          the first entry that has an "mnt-irt:" attribute, by looking in 
 878          the "mnt_irt" table in MySQL for a reference (see the 
 879          rx_node_has_mnt_irt() for details).
 880 
 881          If a reference is found, the list is replaced with a list 
 882          containing the single entry.  If no reference is found, the list
 883          is deleted.
 884   ++++++++++++++++++++++++++++++++++++++*/
 885 void 
 886 mnt_irt_filter (sk_conn_st *condat, 
     /* [<][>][^][v][top][bottom][index][help] */
 887                 SQ_connection_t *sql_connection, 
 888                 GList **datlist)
 889 {
 890     GList *p;
 891     GList *new_datlist;
 892     rx_datcpy_t *rx_data;
 893     int object_id;
 894 
 895     /* empty datlist */
 896     new_datlist = NULL;
 897 
 898     /* search for node with "mnt-irt:" attribute */
 899     for (p=g_list_last(*datlist); p != NULL; p=g_list_previous(p)) {
 900         /* grab the data for this node */
 901         rx_data = (rx_datcpy_t *)p->data;
 902         object_id = rx_data->leafcpy.data_key;
 903 
 904         /* see if this is the node we are looking for */
 905         if (rx_node_has_mnt_irt(condat, sql_connection, object_id)) {
 906             /* use this entry, and remove from old list */
 907             new_datlist = g_list_append(NULL, rx_data);
 908             *datlist = g_list_remove_link(*datlist, p);
 909             g_list_free_1(p);
 910             break;
 911         } 
 912     }
 913 
 914     /* free our old datlist */
 915     for (p=*datlist; p != NULL; p = g_list_next(p)) {
 916         rx_data = (rx_datcpy_t *)p->data;
 917         UT_free(rx_data->leafcpy.data_ptr);
 918         p = g_list_next(p);
 919     }
 920     wr_clear_list(datlist);
 921 
 922     /* use our new datlist */
 923     *datlist = new_datlist;
 924 }
 925 
 926 
 927 /* insert_radix_serials() */
 928 /*++++++++++++++++++++++++++++++++++++++
 929   Insert the radix serial numbers into a temporary table in the database.
 930 
 931   mask_t bitmap The bitmap of attribute to be converted.
 932    
 933   SQ_connection_t *sql_connection The connection to the database.
 934 
 935   char *id_table The id of the temporary table (This is a result of the hacky
 936                   way we've tried to get MySQL to do sub-selects.)
 937   
 938   GList *datlist The list of data from the radix tree.
 939 
 940   More:
 941   +html+ <PRE>
 942   Authors:
 943         ottrey,
 944         marek
 945   +html+ </PRE>
 946 
 947   ++++++++++++++++++++++++++++++++++++++*/
 948 static int insert_radix_serials(sk_conn_st *condat,
     /* [<][>][^][v][top][bottom][index][help] */
 949                                  SQ_connection_t *sql_connection, 
 950                                  char *id_table, GList *datlist) {
 951   GList *qitem;
 952   GString *sql_command;
 953   int serial;
 954   int sql_error;
 955 
 956   sql_error = 0;
 957   for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
 958     rx_datcpy_t *datcpy = qitem->data;
 959 
 960     serial = datcpy->leafcpy.data_key;
 961 
 962     /* don't bother to insert values into our temporary table */
 963     /* if we've lost the client connection */
 964     if ((condat->rtc == 0) && !sql_error) {
 965         sql_command = g_string_sized_new(STR_S);
 966         g_string_sprintf(sql_command, 
 967                          "INSERT INTO %s values (%d)", id_table, serial);
 968         if (SQ_execute_query(sql_connection, sql_command->str, NULL) == -1) {
 969             sql_error = SQ_errno(sql_connection);
 970             report_sql_error(condat, sql_connection, sql_command->str);
 971         }
 972         g_string_free(sql_command, TRUE);
 973     }
 974 
 975     UT_free(datcpy->leafcpy.data_ptr);
 976   }
 977 
 978   wr_clear_list( &datlist );
 979 
 980   /* return error, if any */
 981   return sql_error;
 982 
 983 } /* insert_radix_serials() */
 984 
 985 
 986 /* write_radix_immediate() */
 987 /*++++++++++++++++++++++++++++++++++++++
 988   Display the immediate data carried with the objects returned by the
 989   radix tree.
 990 
 991   GList *datlist      The linked list of dataleaf copies
 992 
 993   sk_conn_st *condat  Connection data for the client
 994 
 995   acc_st  *acc_credit Accounting struct
 996 
 997 More:
 998   +html+ <PRE>
 999   Authors:
1000         marek
1001   +html+ </PRE>
1002 
1003   Also free the list of answers.
1004 ++++++++++++++++++++++++++++++++++++++*/
1005 static void write_radix_immediate(GList *datlist, 
     /* [<][>][^][v][top][bottom][index][help] */
1006                                   sk_conn_st *condat,
1007                                   acc_st    *acc_credit,
1008                                   acl_st    *acl) 
1009 {
1010   GList    *qitem;
1011   
1012   for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
1013     rx_datcpy_t *datcpy = qitem->data;
1014 
1015     SK_cd_puts(condat, datcpy->leafcpy.data_ptr );
1016     SK_cd_puts(condat, "\n");
1017     
1018     UT_free(datcpy->leafcpy.data_ptr);
1019     
1020     AC_count_object(acc_credit, acl, 0 /* public object (private=0) */ );
1021 
1022     if(condat->rtc != 0) {
1023       break;
1024     }
1025   }
1026   
1027   wr_clear_list( &datlist );
1028 } /* write_radix_immediate() */
1029 
1030 
1031 /* map_qc2rx() */
1032 /*++++++++++++++++++++++++++++++++++++++
1033   The mapping between a query_command and a radix query.
1034 
1035   Query_instruction *qi The Query Instruction to be created from the mapping
1036                         of the query command.
1037 
1038   const Query_command *qc The query command to be mapped.
1039 
1040   More:
1041   +html+ <PRE>
1042   Authors:
1043         ottrey,
1044         marek - simplified the logic, added stealth -S option
1045   +html+ </PRE>
1046 
1047   ++++++++++++++++++++++++++++++++++++++*/
1048 static int map_qc2rx(Query_instruction *qi, const Query_command *qc) {
     /* [<][>][^][v][top][bottom][index][help] */
1049   int result=1;
1050   int allflags = (qc->L == 1) + (qc->M == 1) + (qc->l == 1) 
1051                + (qc->m == 1) + (qc->x == 1);
1052 
1053   allflags += qc->c_irt_search;
1054 
1055   qi->rx_keys = qc->keys;
1056 
1057   /* only one option can be active at a time */
1058 
1059   if( allflags > 1 ) {
1060       /* user error  (this should have been checked before) */
1061       
1062       ER_dbg_va(FAC_QI, ASP_QI_SKIP, 
1063                 "ERROR in qc2rx mapping: bad combination of flags");
1064       result = 0;
1065   }
1066   if( allflags == 0 ) { 
1067       /* no options active - default search */
1068       qi->rx_srch_mode = RX_SRCH_EXLESS;
1069       qi->rx_par_a = 0;
1070   }
1071   else if (qc->c_irt_search) {
1072       qi->rx_srch_mode = RX_SRCH_LESS;
1073       qi->rx_par_a = RX_ALL_DEPTHS;
1074   }
1075   else if ( qc->L == 1 ) {
1076       qi->rx_srch_mode = RX_SRCH_LESS;
1077       qi->rx_par_a = RX_ALL_DEPTHS;
1078   }
1079   else if (qc->M == 1) {
1080       qi->rx_srch_mode = RX_SRCH_MORE;
1081       qi->rx_par_a = RX_ALL_DEPTHS;
1082   }
1083   else if (qc->l == 1) {
1084       qi->rx_srch_mode = RX_SRCH_LESS;
1085       qi->rx_par_a = 1;
1086   }
1087   else if (qc->m == 1) {
1088       qi->rx_srch_mode = RX_SRCH_MORE;
1089       qi->rx_par_a = 1;
1090   }
1091   else if (qc->x == 1) {
1092       qi->rx_srch_mode = RX_SRCH_EXACT;
1093       qi->rx_par_a = 0;
1094   }
1095   
1096   if( qi->rx_srch_mode == RX_SRCH_MORE && (qc->S == 1) ) {
1097       qi->rx_srch_mode = RX_SRCH_DBLS;
1098   }
1099   
1100   return result;
1101   
1102 } /* map_qc2rx() */
1103 
1104 
1105 /* run_referral() */
1106 /*++++++++++++++++++++++++++++++++++++++
1107   
1108    invoked when no such domain found. Goes through the domain table
1109    and searches for shorter domains, then if it finds one with referral 
1110    it performs it, otherwise it just returns nothing.
1111 
1112    to perform referral, it actually composes the referral query 
1113    for a given host/port/type and calls the whois query function.
1114 
1115    Well, it returns nothing anyway (void). It just prints to the socket.
1116 
1117   char *ref_host           referral server host name
1118 
1119   unsigned ref_port_int    referral server port number
1120 
1121   char *qry                query to be run
1122 
1123   Author:
1124     marek
1125   ++++++++++++++++++++++++++++++++++++++*/
1126 void run_referral(Query_environ *qe, 
     /* [<][>][^][v][top][bottom][index][help] */
1127                   char *ref_host,
1128                   unsigned ref_port_int,
1129                   char *qry)
1130 {
1131   
1132 #if 1 /* switch off for testing */
1133   er_ret_t err;
1134   char *rep;
1135 
1136         /* WH_sock(sock, host, port, query, maxlines, timeout)) */
1137   err= WH_cd_sock(&(qe->condat), ref_host, ref_port_int, qry, 
1138                   ca_get_referralmaxlines, ca_get_referraltimeout
1139                   );
1140 
1141   switch( err ) {
1142   case SK_OK:
1143     /* OK */
1144     break;
1145   case SK_TIMEOUT:
1146     /* Referral timeout */
1147     rep = ca_get_qi_ref_tmout ;
1148     SK_cd_puts(&(qe->condat), rep);
1149     UT_free(rep);
1150     break;
1151             
1152   case SK_BADHOST:
1153     /* Referral host not found */
1154     rep = ca_get_qi_ref_badhost ;
1155     SK_cd_puts(&(qe->condat), rep);
1156     UT_free(rep);
1157     break;
1158 
1159   case SK_CONNECT:
1160     /* Referral host not responding */
1161     rep = ca_get_qi_ref_hostnottresp ;
1162     SK_cd_puts(&(qe->condat), rep);
1163     UT_free(rep);
1164     break;
1165 
1166   case SK_BIND:
1167   case SK_SOCKET:
1168     /* XXX internal server problem...  */
1169     die; 
1170 
1171   case WH_MAXLINES:
1172     /* Referral reply line limit exceeded */
1173     rep = ca_get_qi_ref_overmaxlin ;
1174     SK_cd_puts(&(qe->condat), rep);
1175     UT_free(rep);
1176     break;
1177     
1178   default: /* any other errors ? */
1179     die;
1180     ;
1181   } /*switch WH_sock */
1182 #endif
1183   
1184 }/*run_referral*/
1185 
1186 
1187 
1188 
1189 
1190 /*++++++++++++++++++++++++++++++++++++++
1191    
1192    prepare and run the referral, displaying the results directly to the
1193    client's connection. 
1194    
1195    XXX still missing protection against a referral loop
1196    XXX handling inverse flag not needed, to be removed
1197 
1198   char *domain               domain being looked up
1199  
1200   Query_instructions *qis    original query instructions structure
1201 
1202   Query_environ *qe          original query environment structure
1203 
1204   Query_instruction *qi      specific query instruction triggered
1205 
1206   SQ_result_set_t *result    result of the lookup containing referral details
1207 
1208   SQ_row_t *row              first row (should be only 1) of the result
1209                              this should contain columns: type, port, host
1210 
1211   char *sourcename           name of the database "source"
1212 
1213   Author: 
1214      marek
1215   ++++++++++++++++++++++++++++++++++++++*/
1216 static
1217 void qi_prep_run_refer(char *domain, 
     /* [<][>][^][v][top][bottom][index][help] */
1218                        Query_instructions *qis,   
1219                        Query_environ *qe, 
1220                        Query_instruction *qi,
1221                        SQ_result_set_t *result, SQ_row_t *row, 
1222                        char *sourcename )
1223 {
1224     int err;
1225     long ref_type;
1226     long ref_port;
1227     char *ref_host; 
1228     GString *querystr;
1229 
1230     /* get values from SQL query */
1231     err = SQ_get_column_int(result, row, 0, &ref_type);
1232     dieif(err);
1233     err = SQ_get_column_int(result, row, 1, &ref_port);
1234     dieif(err);
1235     ref_host = SQ_get_column_string(result, row, 2);
1236     dieif(ref_host == NULL);
1237       
1238     querystr = g_string_sized_new(STR_L);
1239       
1240     /* put -r if the reftype is RIPE and -r or -i were used */
1241     if( (ref_type == RF_RIPE) 
1242         && ( Query[qi->queryindex].querytype == Q_INVERSE       
1243              || qis->recursive > 0  )   ) 
1244     {
1245         g_string_append(querystr, "-r ");
1246     }
1247       
1248     /* prepend with -Vversion,IP for type CLIENTADDRESS */
1249     if( ref_type == RF_CLIENTADDRESS ) {
1250         g_string_sprintf(querystr, "-V%s,%s ", VERSION, qe->condat.ip);
1251     }
1252 
1253       
1254     /* now set the search term - set to the stripped down version 
1255        for inverse query, full-length otherwise */
1256     if( Query[qi->queryindex].querytype == Q_INVERSE ) {
1257         g_string_append(querystr, domain);
1258     }
1259     else {
1260         g_string_append(querystr, qis->qc->keys);
1261     }
1262 
1263     {
1264         /* the object is not from %s, 
1265            it comes from %s %d, use -R to see %s */
1266         char *rep = ca_get_qi_fmt_refheader ;
1267         SK_cd_printf(&(qe->condat), rep, 
1268                      sourcename, 
1269                      ref_host, ref_port,
1270                      sourcename );
1271         UT_free(rep);
1272     }
1273             
1274     /* do the referral */
1275     ER_dbg_va(FAC_QI, ASP_QI_REF_GEN, "referral host is %s", ref_host); 
1276       
1277     run_referral( qe, ref_host, ref_port, querystr->str);
1278       
1279     { /* End of referred query result */
1280         char *rep = ca_get_qi_reftrailer ; 
1281         SK_cd_puts(&(qe->condat), rep);
1282         UT_free(rep);
1283     }
1284     SK_cd_puts(&(qe->condat), "\n");
1285     
1286     g_string_free(querystr, TRUE);
1287     UT_free(ref_host);
1288 }
1289 
1290 
1291 /*++++++++++++++++++++++++++++++++++++++
1292   
1293   specific case of the object ID collection: the domains.
1294   Checks to see if the domain exists, and runs the referral if it is defined
1295   and the domain is missing.
1296 
1297   Arguments:
1298 
1299   char *sourcename                     name of the database "source"
1300 
1301   SQ_connection_t *sql_connection      sql connection dedicated to this thread
1302   
1303   char *id_table                       name of the temporary table to be used
1304   
1305   char *sub_table                      name of the temporary subtable
1306   
1307   Query_instructions *qis    original query instructions structure
1308 
1309   Query_environ *qe          original query environment structure
1310 
1311   Query_instruction *qi      specific query instruction triggered
1312 
1313   acc_st *acc_credit         credit for this client 
1314  
1315   Author:
1316      marek.
1317   ++++++++++++++++++++++++++++++++++++++*/
1318 
1319 static int
1320 qi_collect_domain(char *sourcename,
     /* [<][>][^][v][top][bottom][index][help] */
1321                   SQ_connection_t *sql_connection, 
1322                   char *id_table,
1323                   char *sub_table,
1324                   Query_instructions *qis,   
1325                   Query_environ *qe, 
1326                   Query_instruction *qi,
1327                   acc_st *acc_credit,
1328                   int *sql_error)
1329 {
1330   char *domain = qis->qc->keys;
1331   char *dot = domain;
1332   int subcount = 0;
1333   int foundcount = 0;
1334   GString *sql_command;
1335 
1336   /* we MUST NOT have a diconnection from the server here */
1337   dieif(qe->condat.rtc != 0);
1338 
1339   /* create a string for our queries */
1340   sql_command = g_string_sized_new(STR_XL);
1341 
1342   /* while nothing found and still some pieces of the name left */
1343   while( dot != NULL && subcount == 0 ) { 
1344     int refcount = 0;
1345     SQ_row_t *row;
1346     SQ_result_set_t *result_referrals = NULL;
1347 
1348     ER_dbg_va(FAC_QI, ASP_QI_REF_DET, "run_referral: checking %s", dot);
1349 
1350     /* domain lookup -- query into the _S table */
1351     g_string_sprintf(sql_command, 
1352                      "INSERT INTO %s SELECT object_id "
1353                          "FROM domain "
1354                          "WHERE domain = '%s'",
1355                      sub_table, dot);
1356     if (SQ_execute_query(sql_connection, sql_command->str, NULL) != 0) {
1357         *sql_error = SQ_errno(sql_connection);
1358         report_sql_error(&qe->condat, sql_connection, sql_command->str);
1359         foundcount = 0;
1360         goto exit_qi_collect_domain;
1361     }
1362     subcount = SQ_get_affected_rows(sql_connection); 
1363 
1364     if( subcount != 0 ) { /* domain exists in the database */
1365 
1366         /* referral check. Always done except for -R and INVERSE queries */  
1367         if( qis->qc->R == 0 && 
1368             Query[qi->queryindex].querytype != Q_INVERSE ) {
1369             g_string_sprintf(sql_command, 
1370                              "SELECT type, port, host "
1371                                  "FROM %s ID, refer "
1372                                  "WHERE ID.id = refer.object_id", 
1373                              sub_table);
1374 
1375             if( SQ_execute_query(sql_connection, sql_command->str, 
1376                                     &result_referrals) == -1) 
1377             {
1378                 *sql_error = SQ_errno(sql_connection);
1379                 report_sql_error(&qe->condat, sql_connection, sql_command->str);
1380                 foundcount = 0;
1381                 goto exit_qi_collect_domain;
1382             }
1383             refcount = SQ_num_rows(result_referrals);
1384         }
1385 
1386         /* if referral allowed and defined, even if domain was found but 
1387            contained referral - refer the query */
1388         if( refcount != 0 ) { 
1389             /* get the referral parameters from the first row
1390                and perform it 
1391             */
1392 
1393             row = SQ_row_next(result_referrals);
1394             /* now: query for the original domain */
1395             qi_prep_run_refer(domain,
1396                               qis, qe, qi, result_referrals, row, sourcename);
1397             
1398             acc_credit->referrals -= 1;
1399         }
1400         else {
1401             /* domain found 
1402                and (referral undefined  or  disabled by -R or inverse)
1403                two possible outcomes depending on whether 'dot' is:
1404                * the original search term -> pass what's in _S and quit 
1405                * a 'stripped' domain name -> return no result and quit
1406             */
1407             if( dot == domain ) {
1408                 g_string_sprintf(sql_command, 
1409                                  "INSERT INTO %s SELECT id FROM %s", 
1410                                  id_table, sub_table);
1411                 if (SQ_execute_query(sql_connection, 
1412                                      sql_command->str, NULL) == -1) 
1413                 {
1414                     *sql_error = SQ_errno(sql_connection);
1415                     report_sql_error(&qe->condat, sql_connection, 
1416                                      sql_command->str);
1417                     foundcount = 0;
1418                     goto exit_qi_collect_domain;
1419                 }
1420                 foundcount = SQ_get_affected_rows(sql_connection); 
1421             }
1422         } 
1423         dot = NULL; /* don't make another round */
1424     } /* a domain was found */
1425 
1426     if( result_referrals != NULL ) {
1427         SQ_free_result(result_referrals);
1428         result_referrals = NULL;
1429     }
1430     
1431     if( dot != NULL && (dot=index(dot,'.')) != NULL) {
1432       dot++;
1433     }
1434   }
1435 
1436 /* unified success/failure exit point to perform cleanup */
1437 exit_qi_collect_domain:
1438 
1439   /* free the string we used for queries */
1440   g_string_free(sql_command, TRUE);
1441     
1442   return foundcount;
1443 } /* qi_collect_domain */
1444 
1445 
1446 /* add_ref_name */
1447 /*++++++++++++++++++++++++++++++++++++++
1448 
1449   Creates a SQL query for a reference-by-name lookup. Uses standard name
1450   lookup query generator (create_name_query), so the order of the names
1451   doesn't matter.
1452 
1453   SQ_connection_t *sql_connection   sql connection dedicated to this thread
1454 
1455   char *rectable       table in which to look up
1456   
1457   char *allnames       all name words to be looked up, space delimited.
1458 
1459 ++++++++++++++++++++++++++++++++++++++*/
1460 static
1461 int 
1462 add_ref_name(SQ_connection_t *sql_connection, 
     /* [<][>][^][v][top][bottom][index][help] */
1463              char *rectable,
1464              char *allnames,
1465              sk_conn_st *condat
1466              )
1467 {
1468   int error;
1469 
1470   error = 0;
1471 
1472   /* construct the query, allow zero-length list */
1473   if( strlen(allnames) > 0 ) {
1474     GString *final_query;
1475     GString *select_query;
1476 
1477     final_query = g_string_sized_new(STR_XL);
1478     select_query = g_string_sized_new(STR_XL);
1479 
1480     create_name_query(select_query, "SELECT N00.object_id FROM %s WHERE %s "
1481                       "AND N00.object_type != 100 AND N00.thread_id = 0", 
1482                       allnames);
1483     
1484     g_string_sprintf(final_query, "INSERT INTO %s %s",
1485                      rectable, select_query->str);
1486 
1487     allnames[0]=0;
1488     
1489     if (SQ_execute_query(sql_connection, final_query->str, NULL) == -1 ) {
1490         report_sql_error(condat, sql_connection, final_query->str);
1491         error = SQ_errno(sql_connection);
1492     }
1493 
1494     g_string_free(select_query, TRUE);
1495     g_string_free(final_query, TRUE);
1496   }
1497 
1498   return error;
1499 }/* add_ref_name */
1500 
1501 
1502 
1503 /* qi_collect_ids */
1504 /*++++++++++++++++++++++++++++++++++++++ 
1505   
1506   collects object ID's from all queries defined in the Query_instructions 
1507   array. The results from RADIX trees are maintained in a linked list, the 
1508   results from SQL lookups are kept in a temporary table. For domains,
1509   a specific function is invoked that may run the referral. 
1510   Any sql lookup will be limited to the maximum number of objects allowed
1511   for the client (acl and credit are checked for this).
1512   The routine uses its own temporary _S table, destroyed at exit.
1513   
1514   ca_dbSource_t *dbhdl              source-specific identifier (defined in CA)
1515   
1516   char *sourcename                  name of the database "source"
1517   
1518   SQ_connection_t **sql_connection  sql connection dedicated to this thread
1519                                     (replaced on cancel)
1520  
1521   Query_instructions *qis           original query instructions structure
1522 
1523   Query_environ *qe                 original query environment structure
1524  
1525   char *id_table                    the table to store the ID's found
1526   
1527   GList **datlist                   the list  to store the Radix leaves found
1528   
1529   acc_st *acc_credit                credit for this client 
1530   
1531   acl_st *acl                       acl for this client 
1532   
1533   ++++++++++++++++++++++++++++++++++++++*/
1534 static
1535 int
1536 qi_collect_ids(ca_dbSource_t *dbhdl,
     /* [<][>][^][v][top][bottom][index][help] */
1537                char *sourcename,
1538                SQ_connection_t **sql_connection,
1539                Query_instructions *qis,
1540                Query_environ *qe,       
1541                char *id_table,
1542                GList **datlist,
1543                acc_st *acc_credit,
1544                acl_st *acl
1545                )
1546 {
1547   Query_instruction **ins=NULL;
1548   int i;
1549   int  count, errors=0;
1550   GString *sql_command;
1551   er_ret_t err;
1552   char sub_table[64];
1553   int limit ;
1554              /* a limit on the max number of objects to be returned
1555                 from a single search. For some queries the object types
1556                 are not known at this stage, so the limit must be
1557                 the higher number of the two: private / public,
1558                 or unlimited if any of them is 'unlimited'.
1559              */
1560   char limit_str[32];
1561   int sql_error;
1562 
1563   /* use a nice resizing GString for our command */
1564   sql_command = g_string_sized_new(STR_XL);
1565 
1566   if( (limit = AC_get_higher_limit(acc_credit,acl)) == -1) {
1567     strcpy(limit_str,"");
1568   } else {
1569     sprintf(limit_str," LIMIT %d", limit+1); /* make sure we collect more
1570                                                 so that the client hits
1571                                                 the limit */
1572   }
1573 
1574   sprintf(sub_table, "%s_S ", id_table);
1575   
1576   /* see if there was a leftover table from a crashed session 
1577    * (assume the ID cannot be currently in use)
1578    *
1579    * update: this can't ever happen with TEMPORARY tables, but we're going to
1580    *         check for it anyway  - shane
1581    */
1582   g_string_sprintf(sql_command, "DROP TABLE IF EXISTS %s", sub_table);
1583   if( SQ_execute_query(*sql_connection, sql_command->str, NULL) == -1 ) {
1584       report_sql_error(&qe->condat, *sql_connection, sql_command->str);
1585       return SQ_errno(*sql_connection);
1586   }
1587 
1588   /* create a table for special subqueries (domain only for now) */
1589   g_string_sprintf(sql_command, 
1590                    "CREATE " TEMPORARY " TABLE %s ( id int ) TYPE=HEAP", 
1591                    sub_table);
1592   if( SQ_execute_query(*sql_connection, sql_command->str, NULL) == -1 ) {
1593       report_sql_error(&qe->condat, *sql_connection, sql_command->str);
1594       return SQ_errno(*sql_connection);
1595   }
1596   
1597   /* Iterate through query instructions */
1598   ins = qis->instruction;
1599   sql_error = 0;
1600   for (i=0; ins[i] != NULL && errors == 0; i++) {
1601     Query_instruction *qi = ins[i];
1602     
1603     /* check if the client is still there */
1604     if( qe->condat.rtc ) {
1605       break;
1606     }
1607 
1608     switch ( qi->search_type ) {
1609     case R_SQL:
1610       count = 0;
1611       if ( qi->query_str != NULL ) {
1612 
1613         /* handle special cases first */
1614         if( Query[qi->queryindex].class == C_DN 
1615             && Query[qi->queryindex].querytype == Q_LOOKUP ) {
1616           
1617           /* if any more cases than just domain appear, we will be
1618              cleaning the _S table from the previous query here 
1619              
1620              "DELETE FROM %s_S"
1621           */
1622           
1623           count = qi_collect_domain(sourcename, *sql_connection, id_table, 
1624                                     sub_table, qis, qe, qi, acc_credit, 
1625                                     &sql_error);
1626         } /* if class DN and Straight lookup */
1627         else {
1628           /* any other class of query */
1629 
1630           g_string_sprintf(sql_command, "INSERT INTO %s %s %s", 
1631                   id_table, qi->query_str, limit_str);
1632 
1633           if(sql_execute_watched( &(qe->condat), sql_connection, 
1634                                   sql_command->str, NULL) == -1 ) {
1635             errors++;
1636             sql_error = SQ_errno(*sql_connection);
1637             report_sql_error(&qe->condat, *sql_connection, sql_command->str);
1638           }
1639           count = SQ_get_affected_rows(*sql_connection);
1640         } /* not DN */
1641       } /* if SQL query not NULL */
1642       
1643       ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1644                 "%d entries added in %s query for %s",
1645                 count, Query[qi->queryindex].descr, qis->qc->keys
1646                 );
1647       break;
1648       
1649     case R_RADIX:
1650 
1651      
1652       err = RP_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0, 
1653                           qi->rx_keys, dbhdl, 
1654                           Query[qi->queryindex].attribute, 
1655                           datlist, limit);
1656      
1657 
1658       if( NOERR(err)) {
1659         if( ER_is_traced(FAC_QI, ASP_QI_COLL_DET ) ) {
1660           /* prevent unnecessary g_list_length call */
1661           
1662           ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1663                     "%d entries after %s (mode %d par %d reg %d) query for %s",
1664                     g_list_length(*datlist),
1665                     Query[qi->queryindex].descr,
1666                     qi->rx_srch_mode, qi->rx_par_a, 
1667                     dbhdl,
1668                     qi->rx_keys);
1669         }
1670       }
1671       else {
1672         ER_inf_va(FAC_QI, ASP_QI_COLL_DET,
1673                   "RP_asc_search returned %x ", err);
1674       }
1675       break;
1676       
1677     default: die;
1678     } /* switch */
1679     
1680   } /* for <every instruction> */
1681 
1682   /* Now drop the _S table */
1683   g_string_sprintf(sql_command, "DROP TABLE IF EXISTS %s", sub_table);
1684   if(SQ_execute_query(*sql_connection, sql_command->str, NULL) == -1 ) {
1685       report_sql_error(&qe->condat, *sql_connection, sql_command->str);
1686       sql_error = SQ_errno(*sql_connection);
1687   }
1688 
1689   /* free our command buffer */
1690   g_string_free(sql_command, TRUE);
1691 
1692   /* return success */
1693   return sql_error;
1694 }
1695 
1696 /* qi_fetch_references */ 
1697 /*++++++++++++++++++++++++++++++++++++++
1698   
1699   given the list of object ID's collects the references from these objects 
1700   to person and role objects. Uses its own temporary SQL table (_R)
1701   and upon completion transfers the results from it to the main
1702   temporary table. Runs queries in watched mode, to be able to cancel them.
1703   
1704   SQ_connection_t **sql_connection  sql connection dedicated to this thread
1705                                     (replaced on cancel)
1706   
1707   Query_environ *qe                 original query environment structure
1708   
1709   char *id_table                    the table with the ID's found
1710   
1711   acc_st *acc_credit                credit for this client 
1712   
1713   acl_st *acl                       acl for this client 
1714 
1715 ++++++++++++++++++++++++++++++++++++++*/
1716 static
1717 int
1718 qi_fetch_references(SQ_connection_t **sql_connection,
     /* [<][>][^][v][top][bottom][index][help] */
1719                     Query_environ *qe,
1720                     char *id_table,
1721                     acc_st *acc_credit,
1722                     acl_st *acl
1723                     )
1724 {
1725     char rec_table[64];
1726     SQ_result_set_t *result = NULL;
1727     SQ_row_t *row;
1728     int thisid = 0;
1729     int oldid = 0;
1730     char allnames[STR_L];
1731     char sql_command[STR_XL];
1732     int sql_error;
1733 
1734     /* use sql_error to flag errors */
1735     sql_error = 0;
1736  
1737     sprintf(rec_table, "%s_R", id_table);
1738 
1739     /* see if there was a leftover table from a crashed session 
1740      * (assume the ID cannot be currently in use)
1741      */
1742     sprintf(sql_command, "DROP TABLE IF EXISTS %s ", rec_table);
1743     if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1) {
1744         report_sql_error(&qe->condat, *sql_connection, sql_command);
1745         return SQ_errno(*sql_connection);
1746     }
1747 
1748     /* a temporary table for recursive data must be created, because
1749        a query using the same table as a source and target is illegal
1750        ( like: INSERT into ID_123 SELECT * FROM ID_123,admin_c WHERE ... )
1751     */
1752     sprintf(sql_command, 
1753      "CREATE " TEMPORARY " TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", 
1754      rec_table);
1755     if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1) {
1756         report_sql_error(&qe->condat, *sql_connection, sql_command);
1757         return SQ_errno(*sql_connection);
1758     }
1759 
1760     /* from this point on, we can't just return on error, because 
1761        we need to insure the table we just created gets dropped */
1762     
1763     /* find the contacts */      
1764     sprintf(sql_command, Q_REC, rec_table, id_table, "author");
1765     if (sql_execute_watched( &(qe->condat), sql_connection, sql_command, NULL) 
1766         == -1)
1767     {
1768         sql_error = SQ_errno(*sql_connection);
1769         report_sql_error(&qe->condat, *sql_connection, sql_command);
1770     }
1771     
1772     if (!sql_error) {
1773         sprintf(sql_command, Q_REC, rec_table, id_table, "admin_c");
1774         if (sql_execute_watched(&(qe->condat), sql_connection, sql_command, 
1775             NULL) == -1) 
1776         {
1777             sql_error = SQ_errno(*sql_connection);
1778             report_sql_error(&qe->condat, *sql_connection, sql_command);
1779         }
1780     }
1781     
1782     if (!sql_error) {
1783         sprintf(sql_command, Q_REC, rec_table, id_table, "tech_c");
1784         if (sql_execute_watched(&(qe->condat), sql_connection, sql_command, 
1785             NULL) == -1) 
1786         {
1787             sql_error = SQ_errno(*sql_connection);
1788             report_sql_error(&qe->condat, *sql_connection, sql_command);
1789         }
1790     }
1791 
1792     if (!sql_error) {
1793         sprintf(sql_command, Q_REC, rec_table, id_table, "zone_c");
1794         if (sql_execute_watched(&(qe->condat), sql_connection, sql_command, 
1795             NULL) == -1) 
1796         {
1797             sql_error = SQ_errno(*sql_connection);
1798             report_sql_error(&qe->condat, *sql_connection, sql_command);
1799         }
1800     }
1801     
1802     /* replace references to dummies by references by name */
1803     if (!sql_error) {
1804         sprintf(sql_command, 
1805             " SELECT id, name    FROM %s IDS STRAIGHT_JOIN names "
1806             " WHERE IDS.id = names.object_id "
1807             "      AND names.object_type = 100"
1808             " ORDER BY id",
1809             rec_table);
1810         if (sql_execute_watched(&(qe->condat), sql_connection, sql_command, 
1811             &result) == -1) 
1812         {
1813             sql_error = SQ_errno(*sql_connection);
1814             report_sql_error(&qe->condat, *sql_connection, sql_command);
1815         }
1816     }
1817     
1818     /* well, it might not be -1, but if the watchdog worked then the
1819        result is NULL */
1820     if (!sql_error && (result != NULL)) {
1821       
1822       allnames[0]=0;
1823       /* now go through the results and collect names */
1824       while ( !sql_error &&
1825               (qe->condat.rtc == 0) && 
1826               (row = SQ_row_next(result)) != NULL ) 
1827       {
1828         char *id   = SQ_get_column_string(result, row, 0);
1829         char *name = SQ_get_column_string(result, row, 1);
1830         
1831         thisid = atoi(id);
1832         
1833         /* when the id changes, the name is complete */
1834         if( thisid != oldid && oldid != 0 ) {
1835             sql_error = add_ref_name( *sql_connection, rec_table, allnames,
1836                                       &qe->condat);
1837         }
1838         
1839         strcat(allnames, name);
1840         strcat(allnames, " ");
1841         oldid = thisid;
1842         UT_free(id);
1843         UT_free(name);
1844       }
1845       /* also do the last name */
1846       if (!sql_error) {
1847           sql_error = add_ref_name( *sql_connection, rec_table, allnames,
1848                                     &qe->condat);
1849       }
1850       
1851       SQ_free_result(result); /* we can do it only because the watchdog */
1852       /* has not started between the check for non-NULL result and here */
1853     }
1854     
1855     /* if we've lost connection, don't bother with this extra work */
1856     if (!sql_error && (qe->condat.rtc == 0)) {
1857         /* now copy things back to the main temporary table   */
1858         sprintf(sql_command, "INSERT INTO %s SELECT * FROM %s", 
1859                id_table, rec_table);
1860         if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1861             sql_error = SQ_errno(*sql_connection);
1862             report_sql_error(&qe->condat, *sql_connection, sql_command);
1863         }
1864     }
1865 
1866     /* Now drop the IDS recursive table (try to do this even if
1867        we had an SQL error, to avoid leaving extra tables lying around) */
1868     sprintf(sql_command, "DROP TABLE IF EXISTS %s", rec_table);
1869     if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1870         sql_error = SQ_errno(*sql_connection);
1871         report_sql_error(&qe->condat, *sql_connection, sql_command);
1872     }
1873 
1874     /* return error, if any */
1875     return sql_error;
1876 }
1877 /* qi_fetch_references */ 
1878 
1879 
1880 /* QI_execute() */
1881 /*++++++++++++++++++++++++++++++++++++++
1882   Execute the query instructions.  This is called for each source.
1883   This is linked into MySQL by the fact that MySQL doesn't have sub selects
1884  (yet).  The queries are done in two stages.  Make some temporary tables and
1885   insert into them.  Then use them in the next select.
1886 
1887    
1888   ca_dbSource_t *dbhdl            source-specific identifier (defined in CA)
1889 
1890   Query_instructions *qis         query instructions.
1891                                  
1892   Query_environ *qe               query environment.
1893                                  
1894   acc_st *acc_credit              object display credit 
1895                                  
1896   acl_st *acl                     copy of the original acl for this client 
1897 
1898   More:
1899   +html+ <PRE>
1900   Authors:
1901         ottrey - original version,
1902         marek - the rest.
1903   +html+ </PRE>
1904 ++++++++++++++++++++++++++++++++++++++*/
1905 er_ret_t QI_execute(ca_dbSource_t *dbhdl,
     /* [<][>][^][v][top][bottom][index][help] */
1906                     Query_instructions *qis, 
1907                     Query_environ *qe,  
1908                     acc_st *acc_credit,
1909                     acl_st *acl
1910                     ) 
1911 {
1912   /* those things must be freed after use! */
1913   char *dbhost = ca_get_srcdbmachine(dbhdl);
1914   char *dbname = ca_get_srcdbname(dbhdl);
1915   char *dbuser = ca_get_srcdbuser(dbhdl);
1916   char *dbpass = ca_get_srcdbpassword(dbhdl);
1917   char *srcnam;
1918   unsigned dbport = ca_get_srcdbport(dbhdl);
1919   char id_table[64];
1920   char sql_command[STR_XL];
1921   GList *datlist=NULL;
1922   SQ_connection_t *sql_connection=NULL;
1923   int sql_error;
1924 
1925   sql_connection = SQ_get_connection( dbhost, dbport,
1926                                       dbname, dbuser, dbpass );
1927   /* free parameters when done */
1928   UT_free(dbhost);
1929   UT_free(dbuser);
1930   UT_free(dbpass);
1931 
1932   /* return error if occurred */
1933   if (sql_connection == NULL) {
1934     ER_perror(FAC_QI, QI_CANTDB," database='%s' [%d] %s", 
1935               dbname, SQ_errno(sql_connection), SQ_error(sql_connection));
1936     UT_free(dbname);
1937     return QI_CANTDB;
1938   }
1939   UT_free(dbname);
1940 
1941   /* from here on out, we use the sql_error flag to verify our 
1942      connection to the SQL database is still good */
1943   sql_error = 0;
1944 
1945   sprintf(id_table, "ID_%lu_%u",   mysql_thread_id(sql_connection),
1946           pthread_self());
1947 
1948   /* see if there was a leftover table from a crashed session 
1949    * (assume the ID cannot be currently in use)
1950    */
1951   sprintf(sql_command, "DROP TABLE IF EXISTS %s ", id_table);
1952   if (SQ_execute_query(sql_connection, sql_command, NULL) == -1 ) {
1953       sql_error = SQ_errno(sql_connection);
1954       report_sql_error(&qe->condat, sql_connection, sql_command);
1955   }
1956   
1957   /* create a table for id's of all objects found NOT NULL , UNIQUE(id) */
1958   if (!sql_error) {
1959       sprintf(sql_command, 
1960               "CREATE " TEMPORARY 
1961               " TABLE %s ( id INT PRIMARY KEY NOT NULL ) TYPE=HEAP", id_table);
1962       if (SQ_execute_query(sql_connection, sql_command, NULL) == -1 ) {
1963           sql_error = SQ_errno(sql_connection);
1964           report_sql_error(&qe->condat, sql_connection, sql_command);
1965       }
1966   }
1967 
1968   if (!sql_error) { 
1969       srcnam = ca_get_srcname(dbhdl);
1970       sql_error = qi_collect_ids(dbhdl, srcnam, &sql_connection, qis, qe, 
1971                                  id_table, &datlist, acc_credit, acl);
1972       UT_free(srcnam);
1973   }
1974 
1975   /* post-processing */
1976   if (!sql_error && (qis->filtered == 0)) {
1977       /* start the watchdog just to set the rtc flag */
1978       SK_watch_setclear(&(qe->condat));
1979       SK_watchstart(&(qe->condat));
1980 
1981       /* add radix results (only if -K is not active and still connected) */
1982       if (qe->condat.rtc == 0) {
1983 
1984           if (qis->qc->c_irt_search) {
1985               mnt_irt_filter(&(qe->condat), sql_connection, &datlist);
1986           }
1987 
1988           sql_error = insert_radix_serials(&(qe->condat), 
1989                                            sql_connection, 
1990                                            id_table, 
1991                                            datlist);
1992       }
1993 
1994       SK_watchstop(&(qe->condat));
1995   }
1996 
1997   /* fetch recursive objects (ac,tc,zc,ah) */
1998   if (!sql_error && qis->recursive && (qe->condat.rtc == 0)) {
1999       sql_error = qi_fetch_references(&sql_connection, 
2000                                       qe, 
2001                                       id_table, 
2002                                       acc_credit, 
2003                                       acl);
2004   } /* if recursive */
2005   
2006   /* display */
2007   /* -K filtering: 
2008    * right now only filtering, no expanding sets like write_set_objects() 
2009    */
2010   
2011   /* display the immediate data from the radix tree */
2012   if (!sql_error && (qis->filtered == 1)) {
2013     write_radix_immediate(datlist, &(qe->condat), acc_credit, acl );
2014   }
2015 
2016   /* display objects from the IDs table */
2017   if (!sql_error) {
2018       sql_error = write_objects( &sql_connection, id_table, qis->filtered,
2019                                 qis->fast, &(qe->condat), acc_credit, acl);
2020   }
2021 
2022   /* drop the table */
2023   /* try to do this, even if there is an SQL error */
2024   sprintf(sql_command, "DROP TABLE IF EXISTS %s ", id_table);
2025   if (SQ_execute_query(sql_connection, sql_command, NULL) == -1) {
2026       sql_error = SQ_errno(sql_connection);
2027       report_sql_error(&qe->condat, sql_connection, sql_command);
2028   }
2029 
2030   /* Now disconnect (temporary tables get dropped automatically) */
2031   SQ_close_connection(sql_connection);  
2032 
2033   /* return appropriate value */
2034   if (sql_error) {
2035      return QI_SQLERR;
2036   } else {
2037      return QI_OK;
2038   }
2039 } /* QI_execute() */
2040 
2041 
2042 /* instruction_free() */
2043 /*++++++++++++++++++++++++++++++++++++++
2044   Free the instruction.
2045 
2046   Query_instruction *qi query_instruction to be freed.
2047    
2048   More:
2049   +html+ <PRE>
2050   Authors:
2051         ottrey
2052   +html+ </PRE>
2053   ++++++++++++++++++++++++++++++++++++++*/
2054 static void instruction_free(Query_instruction *qi) {
     /* [<][>][^][v][top][bottom][index][help] */
2055   if (qi != NULL) {
2056     if (qi->query_str != NULL) {
2057       UT_free(qi->query_str);
2058     }
2059     UT_free(qi);
2060   }
2061 } /* instruction_free() */
2062 
2063 /* QI_free() */
2064 /*++++++++++++++++++++++++++++++++++++++
2065   Free the query_instructions.
2066 
2067   Query_instructions *qis Query_instructions to be freed.
2068    
2069   More:
2070   +html+ <PRE>
2071   Authors:
2072         ottrey, marek
2073   +html+ </PRE>
2074   ++++++++++++++++++++++++++++++++++++++*/
2075 void QI_free(Query_instructions *qis) {
     /* [<][>][^][v][top][bottom][index][help] */
2076   int i;
2077 
2078   for (i=0; qis->instruction[i] != NULL; i++) {
2079     instruction_free(qis->instruction[i]);
2080   } 
2081 
2082   if (qis != NULL) {
2083     UT_free(qis);
2084   }
2085 
2086 } /* QI_free() */
2087 
2088 /*++++++++++++++++++++++++++++++++++++++
2089   Determine if this query should be conducted or not.
2090 
2091   If it was an inverse query - if the attribute appears in the query command's bitmap.
2092   If it was a lookup query - if the attribute appears in the object type bitmap or
2093                              disregard if there is no object_type bitmap (Ie object filter).
2094 
2095   mask_t bitmap The bitmap of attribute to be converted.
2096    
2097   const Query_command *qc  The query_command that the instructions are created
2098                            from.
2099   
2100   const Query_t q          The query being considered.
2101   +html+ <PRE>
2102   Authors:
2103         ottrey,
2104         marek.
2105   +html+ </PRE>
2106   ++++++++++++++++++++++++++++++++++++++*/
2107 static int valid_query(const Query_command *qc, const Query_t q) {
     /* [<][>][^][v][top][bottom][index][help] */
2108   int result=0;
2109 
2110   if (MA_isset(qc->keytypes_bitmap, q.keytype) == 1) {
2111     if (q.query != NULL) {
2112       switch (q.querytype) {
2113         case Q_INVERSE:
2114           if (MA_isset(qc->inv_attrs_bitmap, q.attribute) ) {
2115             result = 1;
2116           }
2117         break;
2118 
2119         case Q_LOOKUP:
2120           if (q.class == C_ANY 
2121               || MA_isset(qc->object_type_bitmap, (unsigned) q.class)) {
2122             result=1;
2123           }
2124         break;
2125 
2126         default:
2127           /* XXX */fprintf(stderr, "qi:valid_query() -> Bad querytype\n");
2128       }
2129     }
2130   }
2131 
2132   return result;
2133 } /* valid_query() */
2134 
2135 /* QI_new() */
2136 /*++++++++++++++++++++++++++++++++++++++
2137   Create a new set of query_instructions. Returns an allocated structure which
2138   must be freed after use with QI_free().
2139 
2140   const Query_command *qc The query_command that the instructions are created
2141                           from.
2142 
2143   const Query_environ *qe The environmental variables that they query is being
2144                           performed under.
2145   
2146   +html+ <PRE>
2147   Authors:
2148         ottrey,
2149         marek.
2150   +html+ </PRE>
2151   ++++++++++++++++++++++++++++++++++++++*/
2152 Query_instructions *QI_new(const Query_command *qc, const Query_environ *qe) {
     /* [<][>][^][v][top][bottom][index][help] */
2153   Query_instructions *qis=NULL;
2154   Query_instruction *qi=NULL;
2155   int i_no=0;
2156   int i;
2157   char *query_str;
2158 
2159   qis = (Query_instructions *)UT_calloc(1, sizeof(Query_instructions));
2160 
2161   qis->filtered = qc->filtered;
2162   qis->fast = qc->fast;
2163   qis->recursive = qc->recursive;
2164   qis->qc = (qc);
2165 
2166   
2167   for (i=0; Query[i].query != NULL; i++) {
2168 
2169     /* If a valid query. */
2170     if ( valid_query(qc, Query[i]) == 1) {
2171 
2172       qi = (Query_instruction *)UT_calloc(1, sizeof(Query_instruction));
2173 
2174       qi->queryindex = i;
2175 
2176       /* SQL Query */
2177       if ( Query[i].refer == R_SQL) {
2178         qi->search_type = R_SQL;
2179         query_str = create_query(Query[i], qc);
2180 
2181         if (query_str!= NULL) {
2182           qi->query_str = query_str;
2183           qis->instruction[i_no++] = qi;
2184         }
2185       }
2186       /* Radix Query */
2187       else if (Query[i].refer == R_RADIX) {
2188 
2189          /* no inverse queries should use the RADIX tree */
2190         if (MA_bitcount(qc->inv_attrs_bitmap) > 0) {
2191             continue;
2192         }
2193 
2194         qi->search_type = R_RADIX;
2195         
2196         if (map_qc2rx(qi, qc) == 1) {
2197           int j;
2198           int found=0;
2199           
2200           /* check that there is no such query yet, for example if
2201              more than one keytype (wk) matched */
2202           for (j=0; j<i_no; j++) {
2203             Query_instruction *qij = qis->instruction[j];
2204             
2205             if(    qij->search_type == R_RADIX
2206                    && Query[qij->queryindex].attribute 
2207                    == Query[qi ->queryindex].attribute) {
2208               
2209               found=1;
2210               break;
2211             }
2212           }
2213           
2214           if ( found ) {
2215             /* Discard the Query Instruction */
2216             UT_free(qi);
2217           } 
2218           else {
2219             /* Add the query_instruction to the array */
2220             qis->instruction[i_no++] = qi;
2221           }
2222         }
2223       }
2224       else {
2225           /* ERROR: bad search_type */
2226           die;
2227       }
2228     }
2229   }
2230   qis->instruction[i_no++] = NULL;
2231 
2232 
2233   {  /* tracing */
2234       char *descrstr = QI_queries_to_string(qis);
2235 
2236       ER_dbg_va(FAC_QI, ASP_QI_COLL_GEN, "Queries: %s", descrstr );
2237       UT_free( descrstr );
2238   }
2239 
2240   return qis;
2241 
2242 } /* QI_new() */
2243 
2244 
2245  
2246 
2247 
2248 /*++++++++++++++++++++++++++++++++++++++
2249   
2250   char *QI_queries_to_string    returns a list of descriptions for queries 
2251                                 that will be performed (debugging only).
2252                                 Allocated text, must be freed after use.
2253 
2254   Query_instructions *qis       query instructions structure
2255 
2256   Author:
2257      marek.
2258   ++++++++++++++++++++++++++++++++++++++*/
2259 
2260 char *QI_queries_to_string(Query_instructions *qis)
     /* [<][>][^][v][top][bottom][index][help] */
2261 {
2262    Query_instruction *qi;
2263    int i;
2264    char *resstr;
2265 
2266    resstr = (char *)UT_malloc(2);
2267    strcpy(resstr, "{");
2268 
2269    for( i = 0; ( qi=qis->instruction[i] ) != NULL;  i++ ) {
2270        char *descr = Query[qi->queryindex].descr;
2271        int oldres = strlen( resstr );
2272        
2273        resstr = (char *)UT_realloc(resstr, oldres+strlen(descr)+2);
2274        strcat(resstr, descr);
2275        strcat(resstr, ",");
2276    }
2277    if( i>0 ) {
2278        /* cancel the last comma */
2279        resstr[strlen(resstr)-1] = 0;
2280    }
2281 
2282    resstr = (char *)UT_realloc(resstr, strlen(resstr) + 2);
2283    strcat(resstr, "}");
2284    
2285    return resstr;
2286 }

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