## modules/qi/query_instructions.c

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

## FUNCTIONS

This source file includes following functions.

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

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

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