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.82 $
   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     
1152     wr_free(ref_host);
1153 }
1154 
1155 
1156 /*++++++++++++++++++++++++++++++++++++++
1157   
1158   specific case of the object ID collection: the domains.
1159   Checks to see if the domain exists, and runs the referral if it is defined
1160   and the domain is missing.
1161 
1162   Arguments:
1163 
1164   char *sourcename                     name of the database "source"
1165 
1166   SQ_connection_t *sql_connection      sql connection dedicated to this thread
1167   
1168   char *id_table                       name of the temporary table to be used
1169   
1170   char *sub_table                      name of the temporary subtable
1171   
1172   Query_instructions *qis    original query instructions structure
1173 
1174   Query_environ *qe          original query environment structure
1175 
1176   Query_instruction *qi      specific query instruction triggered
1177 
1178   acc_st *acc_credit         credit for this client 
1179  
1180   Author:
1181      marek.
1182   ++++++++++++++++++++++++++++++++++++++*/
1183 
1184 static int
1185 qi_collect_domain(char *sourcename,
     /* [<][>][^][v][top][bottom][index][help] */
1186                   SQ_connection_t *sql_connection, 
1187                   char *id_table,
1188                   char *sub_table,
1189                   Query_instructions *qis,   
1190                   Query_environ *qe, 
1191                   Query_instruction *qi,
1192                   acc_st *acc_credit,
1193                   int *sql_error)
1194 {
1195   char *domain = qis->qc->keys;
1196   char *dot = domain;
1197   int subcount = 0;
1198   int foundcount = 0;
1199 
1200   /* we MUST NOT have a diconnection from the server here */
1201   dieif(qe->condat.rtc != 0);
1202 
1203   /* while nothing found and still some pieces of the name left */
1204   while( dot != NULL && subcount == 0 ) { 
1205     int refcount = 0;
1206     SQ_row_t *row;
1207     SQ_result_set_t *result_referrals = NULL;
1208     char sql_command[STR_XL];
1209 
1210     ER_dbg_va(FAC_QI, ASP_QI_REF_DET, "run_referral: checking %s", dot);
1211 
1212     /* domain lookup -- query into the _S table */
1213     sprintf(sql_command, "INSERT INTO %s SELECT object_id FROM domain WHERE domain = '%s'", sub_table, dot);
1214     
1215     if( SQ_execute_query(sql_connection, sql_command, NULL) == -1) {
1216         *sql_error = SQ_errno(sql_connection);
1217         report_sql_error(&qe->condat, sql_connection, sql_command);
1218         return 0;
1219     }
1220     subcount = SQ_get_affected_rows(sql_connection); 
1221 
1222     if( subcount != 0 ) { /* domain exists in the database */
1223 
1224         /* referral check. Always done except for -R and INVERSE queries */  
1225         if( qis->qc->R == 0 && 
1226             Query[qi->queryindex].querytype != Q_INVERSE ) {
1227             sprintf(sql_command, "SELECT type, port, host FROM %s ID, refer WHERE ID.id = refer.object_id", sub_table);
1228             if( SQ_execute_query(sql_connection, sql_command, 
1229                                     &result_referrals) == -1) 
1230             {
1231                 *sql_error = SQ_errno(sql_connection);
1232                 report_sql_error(&qe->condat, sql_connection, sql_command);
1233                 return 0;
1234             }
1235             refcount = SQ_num_rows(result_referrals);
1236         }
1237 
1238         /* if referral allowed and defined, even if domain was found but 
1239            contained referral - refer the query */
1240         if( refcount != 0 ) { 
1241             /* get the referral parameters from the first row
1242                and perform it 
1243             */
1244 
1245             row = SQ_row_next(result_referrals);
1246             /* now: query for the original domain */
1247             qi_prep_run_refer(domain,
1248                               qis, qe, qi, result_referrals, row, sourcename);
1249             
1250             acc_credit->referrals -= 1;
1251         }
1252         else {
1253             /* domain found 
1254                and (referral undefined  or  disabled by -R or inverse)
1255                two possible outcomes depending on whether 'dot' is:
1256                * the original search term -> pass what's in _S and quit 
1257                * a 'stripped' domain name -> return no result and quit
1258             */
1259             if( dot == domain ) {
1260                 sprintf(sql_command, "INSERT INTO %s SELECT id FROM %s", 
1261                         id_table, sub_table);
1262                 if (SQ_execute_query(sql_connection, sql_command, NULL) == -1) {
1263                     *sql_error = SQ_errno(sql_connection);
1264                     report_sql_error(&qe->condat, sql_connection, sql_command);
1265                     return 0;
1266                 }
1267                 foundcount = SQ_get_affected_rows(sql_connection); 
1268             }
1269         } 
1270         dot = NULL; /* don't make another round */
1271     } /* a domain was found */
1272 
1273     if( result_referrals != NULL ) {
1274         SQ_free_result(result_referrals);
1275         result_referrals = NULL;
1276     }
1277     
1278     if( dot != NULL && (dot=index(dot,'.')) != NULL) {
1279       dot++;
1280     }
1281   }
1282     
1283   return foundcount;
1284 } /* check_domain */
1285 
1286 
1287 /* add_ref_name */
1288 /*++++++++++++++++++++++++++++++++++++++
1289 
1290   Creates a SQL query for a reference-by-name lookup. Uses standard name
1291   lookup query generator (create_name_query), so the order of the names
1292   doesn't matter.
1293 
1294   SQ_connection_t *sql_connection   sql connection dedicated to this thread
1295 
1296   char *rectable       table in which to look up
1297   
1298   char *allnames       all name words to be looked up, space delimited.
1299 
1300 ++++++++++++++++++++++++++++++++++++++*/
1301 static
1302 int 
1303 add_ref_name(SQ_connection_t *sql_connection, 
     /* [<][>][^][v][top][bottom][index][help] */
1304              char *rectable,
1305              char *allnames,
1306              sk_conn_st *condat
1307              )
1308 {
1309   /* construct the query, allow zero-length list */
1310   if( strlen(allnames) > 0 ) {
1311     char final_query[STR_XL];
1312     char select_query[STR_XL];
1313 
1314     create_name_query(select_query, "SELECT N00.object_id FROM %s WHERE %s "
1315                       "AND N00.object_type != 100 AND N00.thread_id = 0", 
1316                       allnames);
1317     
1318     sprintf(final_query, "INSERT INTO %s %s",
1319             rectable,
1320             select_query);
1321 
1322     allnames[0]=0;
1323     
1324     if (SQ_execute_query(sql_connection, final_query, NULL) == -1 ) {
1325         report_sql_error(condat, sql_connection, final_query);
1326         return SQ_errno(sql_connection);
1327     }
1328 
1329   }
1330 
1331   return 0;
1332 }/* add_ref_name */
1333 
1334 
1335 
1336 /* qi_collect_ids */
1337 /*++++++++++++++++++++++++++++++++++++++ 
1338   
1339   collects object ID's from all queries defined in the Query_instructions 
1340   array. The results from RADIX trees are maintained in a linked list, the 
1341   results from SQL lookups are kept in a temporary table. For domains,
1342   a specific function is invoked that may run the referral. 
1343   Any sql lookup will be limited to the maximum number of objects allowed
1344   for the client (acl and credit are checked for this).
1345   The routine uses its own temporary _S table, destroyed at exit.
1346   
1347   ca_dbSource_t *dbhdl              source-specific identifier (defined in CA)
1348   
1349   char *sourcename                  name of the database "source"
1350   
1351   SQ_connection_t **sql_connection  sql connection dedicated to this thread
1352                                     (replaced on cancel)
1353  
1354   Query_instructions *qis           original query instructions structure
1355 
1356   Query_environ *qe                 original query environment structure
1357  
1358   char *id_table                    the table to store the ID's found
1359   
1360   GList **datlist                   the list  to store the Radix leaves found
1361   
1362   acc_st *acc_credit                credit for this client 
1363   
1364   acl_st *acl                       acl for this client 
1365   
1366   ++++++++++++++++++++++++++++++++++++++*/
1367 static
1368 int
1369 qi_collect_ids(ca_dbSource_t *dbhdl,
     /* [<][>][^][v][top][bottom][index][help] */
1370                char *sourcename,
1371                SQ_connection_t **sql_connection,
1372                Query_instructions *qis,
1373                Query_environ *qe,       
1374                char *id_table,
1375                GList **datlist,
1376                acc_st *acc_credit,
1377                acl_st *acl
1378                )
1379 {
1380   Query_instruction **ins=NULL;
1381   int i;
1382   int  count, errors=0;
1383   char sql_command[STR_XL];
1384   er_ret_t err;
1385   char sub_table[32];
1386   int limit ;
1387              /* a limit on the max number of objects to be returned
1388                 from a single search. For some queries the object types
1389                 are not known at this stage, so the limit must be
1390                 the higher number of the two: private / public,
1391                 or unlimited if any of them is 'unlimited'.
1392              */
1393   char limit_str[32];
1394   int sql_error;
1395 
1396   if( (limit = AC_get_higher_limit(acc_credit,acl)) == -1) {
1397     strcpy(limit_str,"");
1398   } else {
1399     sprintf(limit_str," LIMIT %d", limit+1); /* make sure we collect more
1400                                                 so that the client hits
1401                                                 the limit */
1402   }
1403 
1404   sprintf(sub_table, "%s_S ", id_table);
1405   
1406   /* see if there was a leftover table from a crashed session 
1407    * (assume the ID cannot be currently in use)
1408    *
1409    * update: this can't ever happen with TEMPORARY tables, but we're going to
1410    *         check for it anyway  - shane
1411    */
1412   sprintf(sql_command, "DROP TABLE IF EXISTS %s", sub_table);
1413   if( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1414       report_sql_error(&qe->condat, *sql_connection, sql_command);
1415       return SQ_errno(*sql_connection);
1416   }
1417 
1418   /* create a table for special subqueries (domain only for now) */
1419   sprintf(sql_command, "CREATE " TEMPORARY " TABLE %s ( id int ) TYPE=HEAP", 
1420     sub_table);
1421   if( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1422       report_sql_error(&qe->condat, *sql_connection, sql_command);
1423       return SQ_errno(*sql_connection);
1424   }
1425   
1426   /* Iterate through query instructions */
1427   ins = qis->instruction;
1428   sql_error = 0;
1429   for (i=0; ins[i] != NULL && errors == 0; i++) {
1430     Query_instruction *qi = ins[i];
1431     
1432     /* check if the client is still there */
1433     if( qe->condat.rtc ) {
1434       break;
1435     }
1436 
1437     switch ( qi->search_type ) {
1438     case R_SQL:
1439       if ( qi->query_str != NULL ) {
1440 
1441         /* handle special cases first */
1442         if( Query[qi->queryindex].class == C_DN 
1443             && Query[qi->queryindex].querytype == Q_LOOKUP ) {
1444           
1445           /* if any more cases than just domain appear, we will be
1446              cleaning the _S table from the previous query here 
1447              
1448              "DELETE FROM %s_S"
1449           */
1450           
1451           count = qi_collect_domain(sourcename, *sql_connection, id_table, 
1452                                     sub_table, qis, qe, qi, acc_credit, 
1453                                     &sql_error);
1454         } /* if class DN and Straight lookup */
1455         else {
1456           /* any other class of query */
1457 
1458           sprintf(sql_command, "INSERT INTO %s %s %s", 
1459                   id_table, qi->query_str, limit_str);
1460 
1461           if(sql_execute_watched( &(qe->condat), sql_connection, 
1462                                   sql_command, NULL) == -1 ) {
1463             errors++;
1464             sql_error = SQ_errno(*sql_connection);
1465             report_sql_error(&qe->condat, *sql_connection, sql_command);
1466           }
1467           count = SQ_get_affected_rows(*sql_connection);
1468         } /* not DN */
1469       } /* if SQL query not NULL */
1470       
1471       ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1472                 "%d entries added in %s query for %s",
1473                 count, Query[qi->queryindex].descr, qis->qc->keys
1474                 );
1475       break;
1476       
1477     case R_RADIX:
1478 
1479      
1480       err = RP_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0, 
1481                           qi->rx_keys, dbhdl, 
1482                           Query[qi->queryindex].attribute, 
1483                           datlist, limit);
1484      
1485 
1486       if( NOERR(err)) {
1487         if( ER_is_traced(FAC_QI, ASP_QI_COLL_DET ) ) {
1488           /* prevent unnecessary g_list_length call */
1489           
1490           ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1491                     "%d entries after %s (mode %d par %d reg %d) query for %s",
1492                     g_list_length(*datlist),
1493                     Query[qi->queryindex].descr,
1494                     qi->rx_srch_mode, qi->rx_par_a, 
1495                     dbhdl,
1496                     qi->rx_keys);
1497         }
1498       }
1499       else {
1500         ER_inf_va(FAC_QI, ASP_QI_COLL_DET,
1501                   "RP_asc_search returned %x ", err);
1502       }
1503       break;
1504       
1505     default: die;
1506     } /* switch */
1507     
1508   } /* for <every instruction> */
1509 
1510   /* Now drop the _S table */
1511   sprintf(sql_command, "DROP TABLE IF EXISTS %s", sub_table);
1512   if(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1513       report_sql_error(&qe->condat, *sql_connection, sql_command);
1514       return SQ_errno(*sql_connection);
1515   }
1516 
1517   /* return success */
1518   return sql_error;
1519 }
1520 
1521 /* qi_fetch_references */ 
1522 /*++++++++++++++++++++++++++++++++++++++
1523   
1524   given the list of object ID's collects the references from these objects 
1525   to person and role objects. Uses its own temporary SQL table (_R)
1526   and upon completion transfers the results from it to the main
1527   temporary table. Runs queries in watched mode, to be able to cancel them.
1528   
1529   SQ_connection_t **sql_connection  sql connection dedicated to this thread
1530                                     (replaced on cancel)
1531   
1532   Query_environ *qe                 original query environment structure
1533   
1534   char *id_table                    the table with the ID's found
1535   
1536   acc_st *acc_credit                credit for this client 
1537   
1538   acl_st *acl                       acl for this client 
1539 
1540 ++++++++++++++++++++++++++++++++++++++*/
1541 static
1542 int
1543 qi_fetch_references(SQ_connection_t **sql_connection,
     /* [<][>][^][v][top][bottom][index][help] */
1544                     Query_environ *qe,
1545                     char *id_table,
1546                     acc_st *acc_credit,
1547                     acl_st *acl
1548                     )
1549 {
1550     char rec_table[32];
1551     SQ_result_set_t *result = NULL;
1552     SQ_row_t *row;
1553     int thisid = 0;
1554     int oldid = 0;
1555     char allnames[STR_L];
1556     char sql_command[STR_XL];
1557     int sql_error;
1558 
1559     /* use sql_error to flag errors */
1560     sql_error = 0;
1561  
1562     sprintf(rec_table, "%s_R", id_table);
1563 
1564     /* see if there was a leftover table from a crashed session 
1565      * (assume the ID cannot be currently in use)
1566      */
1567     sprintf(sql_command, "DROP TABLE IF EXISTS %s ", rec_table);
1568     if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1) {
1569         report_sql_error(&qe->condat, *sql_connection, sql_command);
1570         return SQ_errno(*sql_connection);
1571     }
1572 
1573     /* a temporary table for recursive data must be created, because
1574        a query using the same table as a source and target is illegal
1575        ( like: INSERT into ID_123 SELECT * FROM ID_123,admin_c WHERE ... )
1576     */
1577     sprintf(sql_command, 
1578      "CREATE " TEMPORARY " TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", 
1579      rec_table);
1580     if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1) {
1581         report_sql_error(&qe->condat, *sql_connection, sql_command);
1582         return SQ_errno(*sql_connection);
1583     }
1584 
1585     /* from this point on, we can't just return on error, because 
1586        we need to insure the table we just created gets dropped */
1587     
1588     /* find the contacts */      
1589     sprintf(sql_command, Q_REC, rec_table, id_table, "author");
1590     if (sql_execute_watched( &(qe->condat), sql_connection, sql_command, NULL) 
1591         == -1)
1592     {
1593         sql_error = SQ_errno(*sql_connection);
1594         report_sql_error(&qe->condat, *sql_connection, sql_command);
1595     }
1596     
1597     if (!sql_error) {
1598         sprintf(sql_command, Q_REC, rec_table, id_table, "admin_c");
1599         if (sql_execute_watched(&(qe->condat), sql_connection, sql_command, 
1600             NULL) == -1) 
1601         {
1602             sql_error = SQ_errno(*sql_connection);
1603             report_sql_error(&qe->condat, *sql_connection, sql_command);
1604         }
1605     }
1606     
1607     if (!sql_error) {
1608         sprintf(sql_command, Q_REC, rec_table, id_table, "tech_c");
1609         if (sql_execute_watched(&(qe->condat), sql_connection, sql_command, 
1610             NULL) == -1) 
1611         {
1612             sql_error = SQ_errno(*sql_connection);
1613             report_sql_error(&qe->condat, *sql_connection, sql_command);
1614         }
1615     }
1616 
1617     if (!sql_error) {
1618         sprintf(sql_command, Q_REC, rec_table, id_table, "zone_c");
1619         if (sql_execute_watched(&(qe->condat), sql_connection, sql_command, 
1620             NULL) == -1) 
1621         {
1622             sql_error = SQ_errno(*sql_connection);
1623             report_sql_error(&qe->condat, *sql_connection, sql_command);
1624         }
1625     }
1626     
1627     /* replace references to dummies by references by name */
1628     if (!sql_error) {
1629         sprintf(sql_command, 
1630             " SELECT id, name    FROM %s IDS STRAIGHT_JOIN names "
1631             " WHERE IDS.id = names.object_id "
1632             "      AND names.object_type = 100"
1633             " ORDER BY id",
1634             rec_table);
1635         if (sql_execute_watched(&(qe->condat), sql_connection, sql_command, 
1636             &result) == -1) 
1637         {
1638             sql_error = SQ_errno(*sql_connection);
1639             report_sql_error(&qe->condat, *sql_connection, sql_command);
1640         }
1641     }
1642     
1643     /* well, it might not be -1, but if the watchdog worked then the
1644        result is NULL */
1645     if (!sql_error && (result != NULL)) {
1646       
1647       allnames[0]=0;
1648       /* now go through the results and collect names */
1649       while ( !sql_error &&
1650               (qe->condat.rtc == 0) && 
1651               (row = SQ_row_next(result)) != NULL ) 
1652       {
1653         char *id   = SQ_get_column_string(result, row, 0);
1654         char *name = SQ_get_column_string(result, row, 1);
1655         
1656         thisid = atoi(id);
1657         
1658         /* when the id changes, the name is complete */
1659         if( thisid != oldid && oldid != 0 ) {
1660             sql_error = add_ref_name( *sql_connection, rec_table, allnames,
1661                                       &qe->condat);
1662         }
1663         
1664         strcat(allnames, name);
1665         strcat(allnames, " ");
1666         oldid = thisid;
1667         wr_free(id);
1668         wr_free(name);
1669       }
1670       /* also do the last name */
1671       if (!sql_error) {
1672           sql_error = add_ref_name( *sql_connection, rec_table, allnames,
1673                                     &qe->condat);
1674       }
1675       
1676       SQ_free_result(result); /* we can do it only because the watchdog */
1677       /* has not started between the check for non-NULL result and here */
1678     }
1679     
1680     /* if we've lost connection, don't bother with this extra work */
1681     if (!sql_error && (qe->condat.rtc == 0)) {
1682         /* now copy things back to the main temporary table   */
1683         sprintf(sql_command, "INSERT INTO %s SELECT * FROM %s", 
1684                id_table, rec_table);
1685         if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1686             sql_error = SQ_errno(*sql_connection);
1687             report_sql_error(&qe->condat, *sql_connection, sql_command);
1688         }
1689     }
1690 
1691     /* Now drop the IDS recursive table (try to do this even if
1692        we had an SQL error, to avoid leaving extra tables lying around) */
1693     sprintf(sql_command, "DROP TABLE IF EXISTS %s", rec_table);
1694     if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1695         sql_error = SQ_errno(*sql_connection);
1696         report_sql_error(&qe->condat, *sql_connection, sql_command);
1697     }
1698 
1699     /* return error, if any */
1700     return sql_error;
1701 }
1702 /* qi_fetch_references */ 
1703 
1704 
1705 /* QI_execute() */
1706 /*++++++++++++++++++++++++++++++++++++++
1707   Execute the query instructions.  This is called for each source.
1708   This is linked into MySQL by the fact that MySQL doesn't have sub selects
1709  (yet).  The queries are done in two stages.  Make some temporary tables and
1710   insert into them.  Then use them in the next select.
1711 
1712    
1713   ca_dbSource_t *dbhdl            source-specific identifier (defined in CA)
1714 
1715   Query_instructions *qis         query instructions.
1716                                  
1717   Query_environ *qe               query environment.
1718                                  
1719   acc_st *acc_credit              object display credit 
1720                                  
1721   acl_st *acl                     copy of the original acl for this client 
1722 
1723   More:
1724   +html+ <PRE>
1725   Authors:
1726         ottrey - original version,
1727         marek - the rest.
1728   +html+ </PRE>
1729 ++++++++++++++++++++++++++++++++++++++*/
1730 er_ret_t QI_execute(ca_dbSource_t *dbhdl,
     /* [<][>][^][v][top][bottom][index][help] */
1731                     Query_instructions *qis, 
1732                     Query_environ *qe,  
1733                     acc_st *acc_credit,
1734                     acl_st *acl
1735                     ) 
1736 {
1737   /* those things must be freed after use! */
1738   char *dbhost = ca_get_srcdbmachine(dbhdl);
1739   char *dbname = ca_get_srcdbname(dbhdl);
1740   char *dbuser = ca_get_srcdbuser(dbhdl);
1741   char *dbpass = ca_get_srcdbpassword(dbhdl);
1742   char *srcnam;
1743   unsigned dbport = ca_get_srcdbport(dbhdl);
1744   char id_table[STR_S];
1745   char sql_command[STR_XL];
1746   GList *datlist=NULL;
1747   SQ_connection_t *sql_connection=NULL;
1748   int sql_error;
1749 
1750   sql_connection = SQ_get_connection( dbhost, dbport,
1751                                       dbname, dbuser, dbpass );
1752   /* free parameters when done */
1753   wr_free(dbhost);
1754   wr_free(dbuser);
1755   wr_free(dbpass);
1756 
1757   /* return error if occurred */
1758   if (sql_connection == NULL) {
1759     ER_perror(FAC_QI, QI_CANTDB," database='%s' [%d] %s", 
1760               dbname, SQ_errno(sql_connection), SQ_error(sql_connection));
1761     wr_free(dbname);
1762     return QI_CANTDB;
1763   }
1764   wr_free(dbname);
1765 
1766   /* from here on out, we use the sql_error flag to verify our 
1767      connection to the SQL database is still good */
1768   sql_error = 0;
1769 
1770   sprintf(id_table, "ID_%ld_%d",   mysql_thread_id(sql_connection),
1771           pthread_self());
1772 
1773   /* see if there was a leftover table from a crashed session 
1774    * (assume the ID cannot be currently in use)
1775    */
1776   sprintf(sql_command, "DROP TABLE IF EXISTS %s ", id_table);
1777   if (SQ_execute_query(sql_connection, sql_command, NULL) == -1 ) {
1778       sql_error = SQ_errno(sql_connection);
1779       report_sql_error(&qe->condat, sql_connection, sql_command);
1780   }
1781   
1782   /* create a table for id's of all objects found NOT NULL , UNIQUE(id) */
1783   if (!sql_error) {
1784       sprintf(sql_command, 
1785               "CREATE " TEMPORARY 
1786               " TABLE %s ( id INT PRIMARY KEY NOT NULL ) TYPE=HEAP", id_table);
1787       if (SQ_execute_query(sql_connection, sql_command, NULL) == -1 ) {
1788           sql_error = SQ_errno(sql_connection);
1789           report_sql_error(&qe->condat, sql_connection, sql_command);
1790       }
1791   }
1792 
1793   if (!sql_error) { 
1794       srcnam = ca_get_srcname(dbhdl);
1795       sql_error = qi_collect_ids(dbhdl, srcnam, &sql_connection, qis, qe, 
1796                                  id_table, &datlist, acc_credit, acl);
1797       wr_free(srcnam);
1798   }
1799 
1800   /* post-processing */
1801   if (!sql_error && (qis->filtered == 0)) {
1802       /* start the watchdog just to set the rtc flag */
1803       SK_watch_setclear(&(qe->condat));
1804       SK_watchstart(&(qe->condat));
1805 
1806       /* add radix results (only if -K is not active and still connected) */
1807       if (qe->condat.rtc == 0) {
1808           sql_error = insert_radix_serials(&(qe->condat), 
1809                                            sql_connection, 
1810                                            id_table, 
1811                                            datlist);
1812       }
1813 
1814       SK_watchstop(&(qe->condat));
1815   }
1816 
1817   /* fetch recursive objects (ac,tc,zc,ah) */
1818   if (!sql_error && qis->recursive && (qe->condat.rtc == 0)) {
1819       sql_error = qi_fetch_references(&sql_connection, 
1820                                       qe, 
1821                                       id_table, 
1822                                       acc_credit, 
1823                                       acl);
1824   } /* if recursive */
1825   
1826   /* display */
1827   /* -K filtering: 
1828    * right now only filtering, no expanding sets like write_set_objects() 
1829    */
1830   
1831   /* display the immediate data from the radix tree */
1832   if (!sql_error && (qis->filtered == 1)) {
1833     write_radix_immediate(datlist, &(qe->condat), acc_credit, acl );
1834   }
1835 
1836   /* display objects from the IDs table */
1837   if (!sql_error) {
1838       sql_error = write_objects( &sql_connection, id_table, qis->filtered,
1839                                 qis->fast, &(qe->condat), acc_credit, acl);
1840   }
1841 
1842   /* drop the table */
1843   /* try to do this, even if there is an SQL error */
1844   sprintf(sql_command, "DROP TABLE IF EXISTS %s ", id_table);
1845   if (SQ_execute_query(sql_connection, sql_command, NULL) == -1) {
1846       sql_error = SQ_errno(sql_connection);
1847       report_sql_error(&qe->condat, sql_connection, sql_command);
1848   }
1849 
1850   /* Now disconnect (temporary tables get dropped automatically) */
1851   SQ_close_connection(sql_connection);  
1852 
1853   /* return appropriate value */
1854   if (sql_error) {
1855      return QI_SQLERR;
1856   } else {
1857      return QI_OK;
1858   }
1859 } /* QI_execute() */
1860 
1861 
1862 /* instruction_free() */
1863 /*++++++++++++++++++++++++++++++++++++++
1864   Free the instruction.
1865 
1866   Query_instruction *qi query_instruction to be freed.
1867    
1868   More:
1869   +html+ <PRE>
1870   Authors:
1871         ottrey
1872   +html+ </PRE>
1873   ++++++++++++++++++++++++++++++++++++++*/
1874 static void instruction_free(Query_instruction *qi) {
     /* [<][>][^][v][top][bottom][index][help] */
1875   if (qi != NULL) {
1876     if (qi->query_str != NULL) {
1877       wr_free(qi->query_str);
1878     }
1879     wr_free(qi);
1880   }
1881 } /* instruction_free() */
1882 
1883 /* QI_free() */
1884 /*++++++++++++++++++++++++++++++++++++++
1885   Free the query_instructions.
1886 
1887   Query_instructions *qis Query_instructions to be freed.
1888    
1889   More:
1890   +html+ <PRE>
1891   Authors:
1892         ottrey, marek
1893   +html+ </PRE>
1894   ++++++++++++++++++++++++++++++++++++++*/
1895 void QI_free(Query_instructions *qis) {
     /* [<][>][^][v][top][bottom][index][help] */
1896   int i;
1897 
1898   for (i=0; qis->instruction[i] != NULL; i++) {
1899     instruction_free(qis->instruction[i]);
1900   } 
1901 
1902   if (qis != NULL) {
1903     wr_free(qis);
1904   }
1905 
1906 } /* QI_free() */
1907 
1908 /*++++++++++++++++++++++++++++++++++++++
1909   Determine if this query should be conducted or not.
1910 
1911   If it was an inverse query - if the attribute appears in the query command's bitmap.
1912   If it was a lookup query - if the attribute appears in the object type bitmap or
1913                              disregard if there is no object_type bitmap (Ie object filter).
1914 
1915   mask_t bitmap The bitmap of attribute to be converted.
1916    
1917   const Query_command *qc  The query_command that the instructions are created
1918                            from.
1919   
1920   const Query_t q          The query being considered.
1921   +html+ <PRE>
1922   Authors:
1923         ottrey,
1924         marek.
1925   +html+ </PRE>
1926   ++++++++++++++++++++++++++++++++++++++*/
1927 static int valid_query(const Query_command *qc, const Query_t q) {
     /* [<][>][^][v][top][bottom][index][help] */
1928   int result=0;
1929 
1930   if (MA_isset(qc->keytypes_bitmap, q.keytype) == 1) {
1931     if (q.query != NULL) {
1932       switch (q.querytype) {
1933         case Q_INVERSE:
1934           if (MA_isset(qc->inv_attrs_bitmap, q.attribute) ) {
1935             result = 1;
1936           }
1937         break;
1938 
1939         case Q_LOOKUP:
1940           if (q.class == C_ANY 
1941               || MA_isset(qc->object_type_bitmap, (unsigned) q.class)) {
1942             result=1;
1943           }
1944         break;
1945 
1946         default:
1947           /* XXX */fprintf(stderr, "qi:valid_query() -> Bad querytype\n");
1948       }
1949     }
1950   }
1951 
1952   return result;
1953 } /* valid_query() */
1954 
1955 /* QI_new() */
1956 /*++++++++++++++++++++++++++++++++++++++
1957   Create a new set of query_instructions. Returns an allocated structure which
1958   must be freed after use with QI_free().
1959 
1960   const Query_command *qc The query_command that the instructions are created
1961                           from.
1962 
1963   const Query_environ *qe The environmental variables that they query is being
1964                           performed under.
1965   
1966   +html+ <PRE>
1967   Authors:
1968         ottrey,
1969         marek.
1970   +html+ </PRE>
1971   ++++++++++++++++++++++++++++++++++++++*/
1972 Query_instructions *QI_new(const Query_command *qc, const Query_environ *qe) {
     /* [<][>][^][v][top][bottom][index][help] */
1973   Query_instructions *qis=NULL;
1974   Query_instruction *qi=NULL;
1975   int i_no=0;
1976   int i;
1977   char *query_str;
1978 
1979   dieif(wr_calloc( (void **) & qis, 1, sizeof(Query_instructions)) != UT_OK);
1980 
1981   qis->filtered = qc->filtered;
1982   qis->fast = qc->fast;
1983   qis->recursive = qc->recursive;
1984   qis->qc = (qc);
1985 
1986   
1987   for (i=0; Query[i].query != NULL; i++) {
1988 
1989     /* If a valid query. */
1990     if ( valid_query(qc, Query[i]) == 1) {
1991 
1992       dieif( wr_calloc((void **) &qi, 1, sizeof(Query_instruction)) != UT_OK);
1993 
1994       qi->queryindex = i;
1995 
1996       /* SQL Query */
1997       if ( Query[i].refer == R_SQL) {
1998         qi->search_type = R_SQL;
1999         query_str = create_query(Query[i], qc);
2000 
2001         if (query_str!= NULL) {
2002           qi->query_str = query_str;
2003           qis->instruction[i_no++] = qi;
2004         }
2005       }
2006       /* Radix Query */
2007       else if (Query[i].refer == R_RADIX) {
2008         qi->search_type = R_RADIX;
2009         
2010         if (map_qc2rx(qi, qc) == 1) {
2011           int j;
2012           int found=0;
2013           
2014           /* check that there is no such query yet, for example if
2015              more than one keytype (wk) matched */
2016           for (j=0; j<i_no; j++) {
2017             Query_instruction *qij = qis->instruction[j];
2018             
2019             if(    qij->search_type == R_RADIX
2020                    && Query[qij->queryindex].attribute 
2021                    == Query[qi ->queryindex].attribute) {
2022               
2023               found=1;
2024               break;
2025             }
2026           }
2027           
2028           if ( found ) {
2029             /* Discard the Query Instruction */
2030             wr_free(qi);
2031           } 
2032           else {
2033             /* Add the query_instruction to the array */
2034             qis->instruction[i_no++] = qi;
2035           }
2036         }
2037       }
2038       else {
2039           /* ERROR: bad search_type */
2040           die;
2041       }
2042     }
2043   }
2044   qis->instruction[i_no++] = NULL;
2045 
2046 
2047   {  /* tracing */
2048       char *descrstr = QI_queries_to_string(qis);
2049 
2050       ER_dbg_va(FAC_QI, ASP_QI_COLL_GEN, "Queries: %s", descrstr );
2051       wr_free( descrstr );
2052   }
2053 
2054   return qis;
2055 
2056 } /* QI_new() */
2057 
2058 
2059  
2060 
2061 
2062 /*++++++++++++++++++++++++++++++++++++++
2063   
2064   char *QI_queries_to_string    returns a list of descriptions for queries 
2065                                 that will be performed (debugging only).
2066                                 Allocated text, must be freed after use.
2067 
2068   Query_instructions *qis       query instructions structure
2069 
2070   Author:
2071      marek.
2072   ++++++++++++++++++++++++++++++++++++++*/
2073 
2074 char *QI_queries_to_string(Query_instructions *qis)
     /* [<][>][^][v][top][bottom][index][help] */
2075 {
2076    Query_instruction *qi;
2077    int i;
2078    char *resstr = NULL;
2079 
2080    dieif( wr_realloc((void **)&resstr, 2 ) != UT_OK);
2081    strcpy(resstr, "{");
2082 
2083    for( i = 0; ( qi=qis->instruction[i] ) != NULL;  i++ ) {
2084        char *descr = Query[qi->queryindex].descr;
2085        int oldres = strlen( resstr );
2086        
2087        dieif( wr_realloc((void **)&resstr, oldres+strlen(descr)+2) != UT_OK);
2088        strcat(resstr, descr);
2089        strcat(resstr, ",");
2090    }
2091    if( i>0 ) {
2092        /* cancel the last comma */
2093        resstr[strlen(resstr)-1] = 0;
2094    }
2095 
2096    dieif( wr_realloc((void **)&resstr, strlen( resstr ) + 2 ) 
2097           != UT_OK);
2098    strcat(resstr, "}");
2099    
2100    return resstr;
2101 }

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