## modules/qi/query_instructions.c

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

## DEFINITIONS

This source file includes following functions.

1. qi_kill_body
2. sql_execute_watched
3. create_name_query
4. create_asblock_query
5. add_filter
6. create_query
7. QI_fast_output
8. filter
9. write_results
10. __report_sql_error
11. write_objects
12. rx_node_has_mnt_irt
13. mnt_irt_filter
14. insert_radix_serials
15. write_radix_immediate
16. map_qc2rx
17. run_referral
18. qi_prep_run_refer
19. qi_collect_domain
20. add_ref_name
21. qi_collect_ids
22. qi_fetch_references
23. QI_execute
24. instruction_free
25. QI_free
26. valid_query
27. QI_new
28. QI_queries_to_string

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

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