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

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