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

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

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