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

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

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