Source File modules/qi/query_instructions.c

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