## modules/qi/query_instructions.c

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

## FUNCTIONS

This source file includes following functions.

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

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

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