Source File modules/qi/query_instructions.c

    1    | /***************************************
    2    |   $Revision: 1.27 $
    3    |
    4    |
    5    |   Sql module (sq).  This is a mysql implementation of an sql module.
    6    |
    7    |   Status: NOT REVUED, NOT TESTED
    8    |
    9    |   Note: this code has been heavily coupled to MySQL, and may need to be changed
    10   |   (to improve performance) if a new RDBMS is used.
    11   |
    12   |   ******************/ /******************
    13   |   Filename            : query_instructions.c
    14   |   Author              : ottrey@ripe.net
    15   |   OSs Tested          : Solaris
    16   |   Problems            : Moderately linked to MySQL.  Not sure which inverse
    17   |                         attributes each option has.  Would like to modify this
    18   |                         after re-designing the objects module.
    19   |   Comments            : Not sure about the different keytypes.
    20   |   ******************/ /******************
    21   |   Copyright (c) 1999                              RIPE NCC
    22   |
    23   |   All Rights Reserved
    24   |
    25   |   Permission to use, copy, modify, and distribute this software and its
    26   |   documentation for any purpose and without fee is hereby granted,
    27   |   provided that the above copyright notice appear in all copies and that
    28   |   both that copyright notice and this permission notice appear in
    29   |   supporting documentation, and that the name of the author not be
    30   |   used in advertising or publicity pertaining to distribution of the
    31   |   software without specific, written prior permission.
    32   |
    33   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
    34   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
    35   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
    36   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
    37   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    38   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    39   |   ***************************************/
    40   | #include <stdio.h>
    41   | #include <string.h>
    42   | #include <glib.h>
    43   |
    44   | #include "which_keytypes.h"
    45   | #include "query_instructions.h"
    46   | #include "mysql_driver.h"
    47   | #include "rxroutines.h"
    48   | #include "stubs.h"
    49   | #include "constants.h"
    50   |
    51   | /*+ String sizes +*/
    52   | #define STR_S   63
    53   | #define STR_M   255
    54   | #define STR_L   1023
    55   | #define STR_XL  4095
    56   | #define STR_XXL 16383
    57   |
    58   |
    59   | #include "QI_queries.def"
    60   |
    61   | /* log_inst_print() */
    62   | /*++++++++++++++++++++++++++++++++++++++
    63   |   Log the instruction.
    64   |
    65   |   char *str instruction to be logged.
    66   |
    67   |   More:
    68   |   +html+ <PRE>
    69   |   Authors:
    70   |         ottrey
    71   |   +html+ </PRE><DL COMPACT>
    72   |   +html+ <DT>Online References:
    73   |   +html+ <DD><UL>
    74   |   +html+ </UL></DL>
    75   |
    76   |   ++++++++++++++++++++++++++++++++++++++*/
    77   | void log_inst_print(char *str) {
    78   |   FILE *logf;
    79   |
    80   |   if (CO_get_instr_logging() == 1) {
    81   |     if (strcmp(CO_get_instr_logfile(), "stdout") == 0) {
    82   |       printf("%s", str);
    83   |     }
    84   |     else {
    85   |       logf = fopen(CO_get_instr_logfile(), "a");
    86   |       fprintf(logf, "%s", str);
    87   |       fclose(logf);
    88   |     }
    89   |   }
    90   |
    91   | } /* log_inst_print() */
    92   |
    93   | /* create_name_query() */
    94   | /*++++++++++++++++++++++++++++++++++++++
    95   |   Create an sql query for the names table.
    96   |
    97   |   char *query_str
    98   |
    99   |   const char *sql_query
    100  |
    101  |   const char *keys
    102  |
    103  |   More:
    104  |   +html+ <PRE>
    105  |   Authors:
    106  |         ottrey
    107  |   +html+ </PRE><DL COMPACT>
    108  |   +html+ <DT>Online References:
    109  |   +html+ <DD><UL>
    110  |   +html+ </UL></DL>
    111  |
    112  |   ++++++++++++++++++++++++++++++++++++++*/
    113  | static void create_name_query(char *query_str, const char *sql_query, const char *keys) {
    114  |   int i;
    115  |   /* Allocate stuff */
    116  |   GString *result = g_string_sized_new(STR_XXL);
    117  |   GString *from_clause = g_string_sized_new(STR_XL);
    118  |   GString *where_clause = g_string_sized_new(STR_XL);
    119  |   gchar **words = g_strsplit(keys, " ", 0);
    120  |
    121  |   g_string_sprintfa(from_clause, "names N%.2d", 0);
    122  |   g_string_sprintfa(where_clause, "N%.2d.name='%s'", 0, words[0]);
    123  |
    124  |   for (i=1; words[i] != NULL; i++) {
    125  |     g_string_sprintfa(from_clause, ", names N%.2d", i);
    126  |     g_string_sprintfa(where_clause, " AND N%.2d.name='%s' AND N00.object_id = N%.2d.object_id", i, words[i], i);
    127  |   }
    128  |
    129  |   sprintf(query_str, sql_query, from_clause->str, where_clause->str);
    130  |
    131  |   /* Free up stuff */
    132  |   g_strfreev(words);
    133  |   g_string_free(where_clause, TRUE);
    134  |   g_string_free(from_clause, TRUE);
    135  |
    136  | } /* create_name_query() */
    137  |
    138  | static void add_filter(char *query_str, const Query_command *qc) {
    139  |   int i;
    140  |   int qlen;
    141  |   char filter_atom[STR_M];
    142  |
    143  | /*
    144  |   if (MA_bitcount(qc->object_type_bitmap) > 0) {
    145  |     g_string_sprintfa(query_str, " AND (");
    146  |     for (i=0; i < C_END; i++) {
    147  |       if (MA_isset(qc->object_type_bitmap, i)) {
    148  |         g_string_sprintfa(query_str, "i.object_type = %d OR ", DF_get_class_dbase_code(i));
    149  |       }
    150  |     }
    151  |     g_string_truncate(query_str, query_str->len-3);
    152  |     g_string_append_c(query_str, ')');
    153  |   }
    154  | */
    155  |   if (MA_bitcount(qc->object_type_bitmap) > 0) {
    156  |     strcat(query_str, " AND (");
    157  |     for (i=0; i < C_END; i++) {
    158  |       if (MA_isset(qc->object_type_bitmap, i)) {
    159  |         strcpy(filter_atom, "");
    160  |         sprintf(filter_atom, "i.object_type = %d OR ", DF_get_class_dbase_code(i));
    161  |         strcat(query_str, filter_atom);
    162  |       }
    163  |     }
    164  |     qlen = strlen(query_str);
    165  |     query_str[qlen-3] = ')';
    166  |     query_str[qlen-2] = '\0';
    167  |     query_str[qlen-1] = '\0';
    168  |   }
    169  |
    170  | } /* add_filter() */
    171  |
    172  | /* create_query() */
    173  | /*++++++++++++++++++++++++++++++++++++++
    174  |   Create an sql query from the query_command and the matching keytype and the
    175  |   selected inverse attributes.
    176  |   Note this clears the first inv_attribute it sees, so is called sequentially
    177  |   until there are no inv_attributes left.
    178  |
    179  |   WK_Type keytype The matching keytype.
    180  |
    181  |   const Query_command *qc The query command.
    182  |
    183  |   mask_t *inv_attrs_bitmap The selected inverse attributes.
    184  |
    185  |   More:
    186  |   +html+ <PRE>
    187  |   Authors:
    188  |         ottrey
    189  |   +html+ </PRE><DL COMPACT>
    190  |   +html+ <DT>Online References:
    191  |   +html+ <DD><UL>
    192  |   +html+ </UL></DL>
    193  |
    194  |   ++++++++++++++++++++++++++++++++++++++*/
    195  | static char *create_query(const Query_t q, const Query_command *qc) {
    196  |   char *result=NULL;
    197  |   char result_buff[STR_XL];
    198  |   Q_Type_t querytype;
    199  |   int conduct_test = 0;
    200  |
    201  |   if (MA_bitcount(qc->inv_attrs_bitmap) > 0) {
    202  |     querytype = Q_INVERSE;
    203  |   }
    204  |   else {
    205  |     querytype = Q_LOOKUP;
    206  |   }
    207  |
    208  |   if ( (q.query != NULL)
    209  |     && (q.querytype == querytype) ) {
    210  |     conduct_test=1;
    211  |   }
    212  |
    213  |   if (conduct_test == 1) {
    214  |
    215  |     if (q.keytype == WK_NAME) {
    216  |       /* Name queries require special treatment. */
    217  |        create_name_query(result_buff, q.query, qc->keys);
    218  |     }
    219  |     else {
    220  |       sprintf(result_buff, q.query, qc->keys);
    221  |     }
    222  |
    223  |     if (q.class == -1) {
    224  |       /* It is class type ANY so add the object filtering */
    225  |       add_filter(result_buff, qc);
    226  |     }
    227  |
    228  |     result = (char *)malloc(strlen(result_buff)+1);
    229  |     strcpy(result, result_buff);
    230  |   }
    231  |
    232  |   return result;
    233  | } /* create_query() */
    234  |
    235  | /* fast_output() */
    236  | /*++++++++++++++++++++++++++++++++++++++
    237  |   This is for the '-F' flag.
    238  |   It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
    239  |
    240  |   Fast isn't fast anymore - it's just there for compatibility reasons.
    241  |   This could be speed up if there were breaks out of the loops, once it matched something.
    242  |   (Wanna add a goto Marek?  :-) ).
    243  |
    244  |   const char *string The string to be "fast outputed".
    245  |
    246  |   More:
    247  |   +html+ <PRE>
    248  |   Authors:
    249  |         ottrey
    250  |   +html+ </PRE><DL COMPACT>
    251  |   +html+ <DT>Online References:
    252  |   +html+ <DD><UL>
    253  |   +html+ </UL></DL>
    254  |
    255  |   ++++++++++++++++++++++++++++++++++++++*/
    256  | char *fast_output(const char *str) {
    257  |   int i,j;
    258  |   char *result;
    259  |   char result_bit[STR_L];
    260  |   char result_buff[STR_XL];
    261  |   gchar **lines = g_strsplit(str, "\n", 0);
    262  |   char * const *attribute_names;
    263  |   gboolean filtering_an_attribute = FALSE;
    264  |   char *value;
    265  |
    266  |   attribute_names = DF_get_attribute_names();
    267  |
    268  |   strcpy(result_buff, "");
    269  |   for (i=0; attribute_names[i] != NULL; i++) {
    270  |     for (j=0; lines[j] != NULL; j++) {
    271  |       if (strncmp(attribute_names[i], lines[j], strlen(attribute_names[i])) == 0) {
    272  |         strcpy(result_bit, "");
    273  |         /* This is the juicy bit that converts the likes of; "source: RIPE" to "*so: RIPE" */
    274  |         value = strchr(lines[j], ':');
    275  |         value++;
    276  |         /* Now get rid of whitespace. */
    277  |         while (*value == ' ' || *value == '\t') {
    278  |           value++;
    279  |         }
    280  |         sprintf(result_bit, "*%s: %s\n", DF_get_attribute_code(i), value);
    281  |         strcat(result_buff, result_bit);
    282  |       }
    283  |       else if (filtering_an_attribute == TRUE) {
    284  |         switch (lines[j][0]) {
    285  |           case ' ':
    286  |           case '\t':
    287  |           case '+':
    288  |             strcpy(result_bit, "");
    289  |             sprintf(result_bit, "%s\n", lines[j]);
    290  |             strcat(result_buff, result_bit);
    291  |           break;
    292  |
    293  |           default:
    294  |             filtering_an_attribute = FALSE;
    295  |         }
    296  |       }
    297  |     }
    298  |   }
    299  |
    300  |   result = (char *)malloc(strlen(result_buff)+1);
    301  |   strcpy(result, result_buff);
    302  |
    303  |   return result;
    304  | } /* fast_output() */
    305  |
    306  | /* filter() */
    307  | /*++++++++++++++++++++++++++++++++++++++
    308  |   Basically it's for the '-K' flag for non-set (and non-radix) objects.
    309  |   It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
    310  |
    311  |   This could be speed up if there were breaks out of the loops, once it matched something.
    312  |   (Wanna add a goto Marek?  :-) ).
    313  |
    314  |   const char *string The string to be filtered.
    315  |
    316  |   More:
    317  |   +html+ <PRE>
    318  |   Authors:
    319  |         ottrey
    320  |   +html+ </PRE><DL COMPACT>
    321  |   +html+ <DT>Online References:
    322  |   +html+ <DD><UL>
    323  |   +html+ </UL></DL>
    324  |
    325  |   ++++++++++++++++++++++++++++++++++++++*/
    326  | char *filter(const char *str) {
    327  |   int i,j;
    328  |   char *result;
    329  |   char result_bit[STR_L];
    330  |   char result_buff[STR_XL];
    331  |   gchar **lines = g_strsplit(str, "\n", 0);
    332  |   char * const *filter_names;
    333  |   gboolean filtering_an_attribute = FALSE;
    334  |
    335  |   filter_names = DF_get_filter_names();
    336  |
    337  |   strcpy(result_buff, "");
    338  |   for (i=0; filter_names[i] != NULL; i++) {
    339  |     for (j=0; lines[j] != NULL; j++) {
    340  |       if (strncmp(filter_names[i], lines[j], strlen(filter_names[i])) == 0) {
    341  |         strcpy(result_bit, "");
    342  |         sprintf(result_bit, "%s\n", lines[j]);
    343  |         strcat(result_buff, result_bit);
    344  |         filtering_an_attribute = TRUE;
    345  |       }
    346  |       else if (filtering_an_attribute == TRUE) {
    347  |         switch (lines[j][0]) {
    348  |           case ' ':
    349  |           case '\t':
    350  |           case '+':
    351  |             strcpy(result_bit, "");
    352  |             sprintf(result_bit, "%s\n", lines[j]);
    353  |             strcat(result_buff, result_bit);
    354  |           break;
    355  |
    356  |           default:
    357  |             filtering_an_attribute = FALSE;
    358  |         }
    359  |       }
    360  |     }
    361  |   }
    362  |
    363  |   result = (char *)malloc(strlen(result_buff)+1);
    364  |   strcpy(result, result_buff);
    365  |
    366  |   return result;
    367  | } /* filter() */
    368  |
    369  | /* write_results() */
    370  | /*++++++++++++++++++++++++++++++++++++++
    371  |   Write the results to the client socket.
    372  |
    373  |   SQ_result_set_t *result The result set returned from the sql query.
    374  |   unsigned filtered       if the objects should go through a filter (-K)
    375  |   sk_conn_st *condat      Connection data for the client
    376  |   int maxobjects          max # of objects to write
    377  |
    378  |   XXX NB. this is very dependendant on what rows are returned in the result!!!
    379  |
    380  |   More:
    381  |   +html+ <PRE>
    382  |   Authors:
    383  |         ottrey
    384  |   +html+ </PRE><DL COMPACT>
    385  |   +html+ <DT>Online References:
    386  |   +html+ <DD><UL>
    387  |   +html+ </UL></DL>
    388  |
    389  |   ++++++++++++++++++++++++++++++++++++++*/
    390  | static int write_results(SQ_result_set_t *result,
    391  | 			 unsigned filtered,
    392  | 			 unsigned fast,
    393  | 			 sk_conn_st *condat,
    394  | 			 int maxobjects /* -1 == unlimited */
    395  | 			 ) {
    396  |   SQ_row_t *row;
    397  |   char *str;
    398  |   char *filtrate;
    399  |   char *fasted;
    400  |   char log_str[STR_L];
    401  |   int retrieved_objects=0;
    402  |
    403  |   /* Get all the results - one at a time */
    404  |   if (result != NULL) {
    405  |     while ( (row = SQ_row_next(result)) != NULL
    406  | 	    && (maxobjects == -1 || retrieved_objects < maxobjects) ) {
    407  |       str = SQ_get_column_string(result, row, 0);
    408  |       if (str != NULL) {
    409  |         strcpy(log_str, "");
    410  |         sprintf(log_str, "Retrieved serial id = %d\n", atoi(str));
    411  |         log_inst_print(log_str);
    412  |       }
    413  |       free(str);
    414  |
    415  |       str = SQ_get_column_string(result, row, 2);
    416  |       if (str != NULL) {
    417  |
    418  |         /* The fast output stage */
    419  |         if (fast == 1) {
    420  |           fasted = fast_output(str);
    421  |           free(str);
    422  |           str = fasted;
    423  |         }
    424  |
    425  |         /* The filtering stage */
    426  |         if (filtered == 0) {
    427  |           SK_cd_puts(condat, str);
    428  |         }
    429  |         else {
    430  |           filtrate = filter(str);
    431  |           SK_cd_puts(condat, filtrate);
    432  |           free(filtrate);
    433  |         }
    434  |         SK_cd_puts(condat, "\n");
    435  |         retrieved_objects++;
    436  |       }
    437  |       free(str);
    438  |     }
    439  |   }
    440  |
    441  |   return retrieved_objects;
    442  | } /* write_results() */
    443  |
    444  | /* write_objects() */
    445  | /*++++++++++++++++++++++++++++++++++++++
    446  |   This is linked into MySQL by the fact that MySQL doesn't have sub selects
    447  |   (yet).  The queries are done in two stages.  Make some temporary tables and
    448  |   insert into them.  Then use them in the next select.
    449  |
    450  |   SQ_connection_t *sql_connection The connection to the database.
    451  |
    452  |   char *id_table The id of the temporary table (This is a result of the hacky
    453  |                   way we've tried to get MySQL to do sub-selects.)
    454  |
    455  |   unsigned int recursive A recursive query.
    456  |
    457  |   sk_conn_st *condat  Connection data for the client
    458  |
    459  |   More:
    460  |   +html+ <PRE>
    461  |   Authors:
    462  |         ottrey
    463  |   +html+ </PRE><DL COMPACT>
    464  |   ++++++++++++++++++++++++++++++++++++++*/
    465  | static void write_objects(SQ_connection_t *sql_connection,
    466  | 			  char *id_table,
    467  | 			  unsigned int recursive,
    468  | 			  unsigned int filtered,
    469  | 			  unsigned int fast,
    470  | 			  sk_conn_st *condat,
    471  | 			  acc_st    *acc_credit
    472  | 			  ) {
    473  |   /* XXX This should really return a linked list of the objects */
    474  |
    475  |   SQ_result_set_t *result;
    476  |   int retrieved_objects=0;
    477  |   int retrieved_contacts=0;
    478  |
    479  |   char sql_command[STR_XL];
    480  |   char log_str[STR_L];
    481  |
    482  |   strcpy(sql_command, "");
    483  |   /* XXX These may and should change a lot. */
    484  |   sprintf(sql_command, Q_OBJECTS, id_table, id_table);
    485  |   result = SQ_execute_query(SQ_STORE, sql_connection, sql_command);
    486  |
    487  |   retrieved_objects += write_results(result, filtered, fast, condat,
    488  | 				     acc_credit->public_objects);
    489  |
    490  |   if( retrieved_objects < SQ_num_rows(result) ) {
    491  |     SK_cd_puts(condat,
    492  |      "% You have reached the limit of returned contact information objects.\n"
    493  |      "% This connection will be terminated now.\n"
    494  |      "% This is a mechanism to prevent abusive use of contact data in the RIPE Database.\n"
    495  |      "% You will not be allowed to query for more CONTACT information for a while.\n"
    496  |      "% Continued attempts to return excessive amounts of contact\n"
    497  |      "% information will result in permanent denial of service.\n"
    498  |      "% Please do not try to use CONTACT information information in the\n"
    499  |      "% RIPE Database for non-operational purposes.\n"
    500  |      "% Refer to http://www.ripe.net/db/dbcopyright.html for more information.\n"
    501  |     );
    502  |   }
    503  |   SQ_free_result(result);
    504  |
    505  |   /* Now for recursive queries */
    506  |   if (recursive == 1) {
    507  |
    508  |     /* create a table for recursive data */
    509  |     sprintf(sql_command, "CREATE TABLE %s_R ( id int ) TYPE=HEAP", id_table);
    510  |     SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
    511  |
    512  |     /* find the contacts */
    513  |     sprintf(sql_command, Q_REC, id_table, "admin_c", id_table, id_table);
    514  |     SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
    515  |
    516  |     sprintf(sql_command, Q_REC, id_table, "tech_c", id_table, id_table);
    517  |     SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
    518  |
    519  |     sprintf(sql_command, Q_REC, id_table, "zone_c", id_table, id_table);
    520  |     SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
    521  |
    522  |     /* XXX These may and should change a lot. */
    523  |     sprintf(sql_command, Q_REC_OBJECTS, id_table, id_table, id_table);
    524  |     result = SQ_execute_query(SQ_STORE, sql_connection, sql_command);
    525  |
    526  |     retrieved_contacts += write_results(result, filtered, fast, condat,
    527  | 					acc_credit->private_objects);
    528  |     if( retrieved_contacts < SQ_num_rows(result) ) {
    529  |       SK_cd_puts(condat,
    530  |        "% The contact data has been truncated due to inadequate privileges\n"
    531  |        "% The amount of data you have requested is considered excessive\n"
    532  |        "% You will be denied to get any more contact objects for some time\n"
    533  |        "% Please do not check if the access has been restored more often\n"
    534  |        "% than once every few hours or you will be denied access permanently\n"
    535  |       );
    536  |     }
    537  |     SQ_free_result(result);
    538  |
    539  |     /* Now drop the IDS recursive table */
    540  |     sprintf(sql_command, "DROP TABLE %s_R", id_table);
    541  |     SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
    542  |
    543  |     acc_credit->private_objects -= retrieved_contacts;
    544  |   }
    545  |
    546  |   /* If nothing is retreived default to return the 0th object - I.e "not found" */
    547  |   if (retrieved_objects == 0) {
    548  |     result = SQ_execute_query(SQ_STORE, sql_connection, Q_NO_OBJECTS);
    549  |
    550  |     if (result != NULL) {
    551  |       write_results(result, filtered, fast, condat, 1);
    552  |       SQ_free_result(result);
    553  |     }
    554  |     else {
    555  |       fprintf(stderr, "QI - ERROR: Couldn't perform no objects query.\n");
    556  |     }
    557  |   }
    558  |   else {
    559  |     acc_credit->public_objects -= retrieved_objects;
    560  |   }
    561  |
    562  |   sprintf(log_str, "%d public / %d contact object(s) retrieved\n\n",
    563  | 	  retrieved_objects, retrieved_contacts );
    564  |   log_inst_print(log_str);
    565  |
    566  | } /* write_objects() */
    567  |
    568  | /* insert_radix_serials() */
    569  | /*++++++++++++++++++++++++++++++++++++++
    570  |   Insert the radix serial numbers into a temporary table in the database.
    571  |
    572  |   mask_t bitmap The bitmap of attribute to be converted.
    573  |
    574  |   SQ_connection_t *sql_connection The connection to the database.
    575  |
    576  |   char *id_table The id of the temporary table (This is a result of the hacky
    577  |                   way we've tried to get MySQL to do sub-selects.)
    578  |
    579  |   GList *datlist The list of data from the radix tree.
    580  |
    581  |   XXX Hmmmmm this isn't really a good place to free things... infact it's quite nasty.  :-(
    582  |
    583  |   More:
    584  |   +html+ <PRE>
    585  |   Authors:
    586  |         ottrey
    587  |   +html+ </PRE><DL COMPACT>
    588  |   +html+ <DT>Online References:
    589  |   +html+ <DD><UL>
    590  |              <LI><A HREF="http://www.gtk.org/rdp/glib/glib-doubly-linked-lists.html">Glist</A>
    591  |   +html+ </UL></DL>
    592  |
    593  |   ++++++++++++++++++++++++++++++++++++++*/
    594  | static void insert_radix_serials(SQ_connection_t *sql_connection, char *id_table, GList *datlist) {
    595  |   GList    *qitem;
    596  |   char sql_command[STR_XL];
    597  |   int serial;
    598  |   int i;
    599  |
    600  |   for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
    601  |     rx_datcpy_t *datcpy = qitem->data;
    602  |
    603  |     serial = datcpy->leafcpy.data_key;
    604  |
    605  |     sprintf(sql_command, "INSERT INTO %s values (%d)", id_table, serial);
    606  |     SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
    607  |
    608  |     wr_free(datcpy->leafcpy.data_ptr);
    609  |   }
    610  |
    611  |   g_list_foreach(datlist, rx_free_list_element, NULL);
    612  |   g_list_free(datlist);
    613  |
    614  | } /* insert_radix_serials() */
    615  |
    616  |
    617  | /* write_radix_immediate() */
    618  | /*++++++++++++++++++++++++++++++++++++++
    619  |   Display the immediate data carried with the objects returned by the
    620  |   radix tree.
    621  |
    622  |   GList *datlist
    623  |   sk_conn_st *condat  Connection data for the client
    624  | More:
    625  |   +html+ <PRE>
    626  |   Authors:
    627  |         marek
    628  |   +html+ </PRE><DL COMPACT>
    629  |   +html+ <DT>Online References:
    630  |   +html+ <DD><UL>
    631  |   +html+ </UL></DL>
    632  |
    633  |
    634  |   Also free the list of answers.
    635  | */
    636  | static void write_radix_immediate(GList *datlist, sk_conn_st *condat)
    637  | {
    638  |   GList    *qitem;
    639  |
    640  |   for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
    641  |     rx_datcpy_t *datcpy = qitem->data;
    642  |
    643  |     SK_cd_puts(condat, datcpy->leafcpy.data_ptr );
    644  |     SK_cd_puts(condat, "\n");
    645  |
    646  |     wr_free(datcpy->leafcpy.data_ptr);
    647  |   }
    648  |
    649  |   g_list_foreach(datlist, rx_free_list_element, NULL);
    650  |   g_list_free(datlist);
    651  | } /* write_radix_immediate() */
    652  |
    653  |
    654  | /* map_qc2rx() */
    655  | /*++++++++++++++++++++++++++++++++++++++
    656  |   The mapping between a query_command and a radix query.
    657  |
    658  |   Query_instruction *qi The Query Instruction to be created from the mapping
    659  |                         of the query command.
    660  |
    661  |   const Query_command *qc The query command to be mapped.
    662  |
    663  |   More:
    664  |   +html+ <PRE>
    665  |   Authors:
    666  |         ottrey
    667  |   +html+ </PRE><DL COMPACT>
    668  |   +html+ <DT>Online References:
    669  |   +html+ <DD><UL>
    670  |   +html+ </UL></DL>
    671  |
    672  |   ++++++++++++++++++++++++++++++++++++++*/
    673  | static int map_qc2rx(Query_instruction *qi, const Query_command *qc) {
    674  |   int result=0;
    675  |   char log_str[STR_XL];
    676  |
    677  |   qi->rx_keys = qc->keys;
    678  |
    679  |   if (MA_bitcount(qc->object_type_bitmap) == 0) {
    680  |     /* Ie. there was no object typed specified with the -T flag. */
    681  |     result=1;
    682  |   }
    683  |   else {
    684  |     switch(qi->family) {
    685  |       case RX_FAM_IN:
    686  |         if (MA_isset(qc->object_type_bitmap, C_IN)) {
    687  |           result=1;
    688  |         }
    689  |       break;
    690  |
    691  |       case RX_FAM_RT:
    692  |         if (MA_isset(qc->object_type_bitmap, C_RT)) {
    693  |           result=1;
    694  |         }
    695  |       break;
    696  |
    697  |       default:
    698  |         fprintf(stderr, "ERROR: Bad family type in radix query\n");
    699  |     }
    700  |   }
    701  |
    702  |   if (result == 1) {
    703  |     if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
    704  |       qi->rx_srch_mode = RX_SRCH_EXLESS;
    705  |       qi->rx_par_a = 0;
    706  |     }
    707  |     else if ( (qc->L == 1) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
    708  |       qi->rx_srch_mode = RX_SRCH_LESS;
    709  |       qi->rx_par_a = RX_ALL_DEPTHS;
    710  |     }
    711  |     else if ( (qc->L == 0) && (qc->M == 1) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
    712  |       qi->rx_srch_mode = RX_SRCH_MORE;
    713  |       qi->rx_par_a = RX_ALL_DEPTHS;
    714  |     }
    715  |     else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 1) && (qc->m == 0) && (qc->x == 0) ) {
    716  |       qi->rx_srch_mode = RX_SRCH_LESS;
    717  |       qi->rx_par_a = 1;
    718  |     }
    719  |     else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 1) && (qc->x == 0) ) {
    720  |       qi->rx_srch_mode = RX_SRCH_MORE;
    721  |       qi->rx_par_a = 1;
    722  |     }
    723  |     else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 1) ) {
    724  |       qi->rx_srch_mode = RX_SRCH_EXACT;
    725  |       qi->rx_par_a = 0;
    726  |     }
    727  |     else {
    728  |       sprintf(log_str, "ERROR in qc2rx mapping: bad combination of flags\n");
    729  |       log_inst_print(log_str);
    730  |       result = 0;
    731  |     }
    732  |   }
    733  |
    734  |   return result;
    735  |
    736  | } /* map_qc2rx() */
    737  |
    738  | /* run_referral() */
    739  | /*
    740  |    invoked when no such domain found. Goes through the domain table
    741  |    and searches for shorter domains, then if it finds one with referral
    742  |    it performs it, otherwise it just returns nothing.
    743  |
    744  |    to perform referral, it actually composes the referral query
    745  |    for a given host/port/type and calls the whois query function.
    746  |
    747  |    Well, it returns nothing anyway (void). It just prints to the socket.
    748  |
    749  | */
    750  | void run_referral(SQ_connection_t *sql_connection, Query_instructions *qis,   Query_environ *qe, int qi_index) {
    751  |   char *dot = qis->qc->keys;
    752  |   char querystr[STR_L];
    753  |   char *query;
    754  |   char log_str[STR_L];
    755  |   SQ_row_t *row;
    756  |   SQ_result_set_t *result;
    757  |   char sql_command[STR_XL];
    758  |   int i;
    759  |   int stop_loop=0;
    760  |   char *ref_host;
    761  |   char *ref_type;
    762  |   char *ref_port;
    763  |   int  ref_port_int;
    764  |
    765  |   strcpy(querystr,"");
    766  |
    767  |   while( !stop_loop && (dot=index(dot,'.')) != NULL ) {
    768  |     dot++;
    769  |
    770  |     sprintf(log_str, "run_referral: checking %s\n", dot);
    771  |     log_inst_print(log_str);
    772  |
    773  | /* Changed for RIPE4 - ottrey 27/12/1999
    774  |     sprintf(sql_command, "SELECT * FROM domain WHERE domain = '%s'", dot);
    775  | */
    776  |     sprintf(sql_command, "SELECT domain.object_id, domain, type, port, host FROM domain, refer WHERE domain.object_id = refer.object_id AND domain = '%s'", dot);
    777  |     result = SQ_execute_query(SQ_STORE, sql_connection, sql_command);
    778  |
    779  |     switch( SQ_num_rows(result) ) {
    780  |       case 0: /* no such domain -> no action, will try next chunk */
    781  |       break;
    782  |
    783  |       case 1: /* check for referral host and perform query if present
    784  |                in any case end the loop */
    785  |       stop_loop=1;
    786  |       assert( (row = SQ_row_next(result)) != NULL);
    787  |
    788  |       ref_host = SQ_get_column_string(result, row, 4);
    789  |       sprintf(log_str, "referral host is >%s<\n",ref_host);
    790  |       log_inst_print(log_str);
    791  |       if( ref_host != NULL && strlen(ref_host) > 0 ) {
    792  |         ref_type = SQ_get_column_string(result, row, 2);
    793  |         ref_port = SQ_get_column_string(result, row, 3);
    794  |
    795  |         /* get the integer value, it should be correct */
    796  |         if( sscanf( ref_port, "%d",&ref_port_int) < 1 ) {
    797  |           die;
    798  |         }
    799  |
    800  |         /* compose the query: */
    801  |
    802  |         /* put -r if the reftype is RIPE and -r or -i were used */
    803  |         if( strcmp(ref_type,"RIPE") == 0
    804  |             && (   Query[qis->instruction[qi_index]->queryindex]
    805  |                    .querytype == Q_INVERSE
    806  |                    || qis->recursive > 0  )   ) {
    807  |           strcat(querystr," -r ");
    808  |         }
    809  |
    810  |         /* prepend with -Vversion,IP for type CLIENTADDRESS */
    811  |         if( strcmp(ref_type,"CLIENTADDRESS") == 0 ) {
    812  |           char optv[STR_M];
    813  |
    814  |           snprintf(optv,STR_M," -V%s,%s ","RIP0.88", qe->condat.ip);
    815  |           strcat(querystr,optv);
    816  |         }
    817  |
    818  |         /* now set the search term - set to the stripped down version
    819  |            for inverse query, full-length otherwise */
    820  |         if( Query[qis->instruction[qi_index]->queryindex].querytype == Q_INVERSE ) {
    821  |           strcat(querystr,dot);
    822  |         }
    823  |         else {
    824  |           strcat(querystr,qis->qc->keys);
    825  |         }
    826  |
    827  |         /* WH_sock(sock, host, port, query, maxlines, timeout)) */
    828  |         switch( WH_sock(qe->condat.sock, ref_host, ref_port_int, querystr,  25, 5) ) {
    829  |           case WH_TIMEOUT:
    830  |           SK_cd_puts(&(qe->condat),"referral timeout\n");
    831  |           break;
    832  |
    833  |           case WH_MAXLINES:
    834  |           SK_cd_puts(&(qe->condat),"referral maxlines exceeded\n");
    835  |           break;
    836  |
    837  |           default:
    838  |           ;
    839  |         } /*switch WH_sock */
    840  |       }
    841  |       break;
    842  |
    843  |       default: /* more than one domain in this file: something broken */
    844  |       die;
    845  |     }
    846  |     SQ_free_result(result);
    847  |   }
    848  | } /*run_referral*/
    849  |
    850  | /* QI_execute() */
    851  | /*++++++++++++++++++++++++++++++++++++++
    852  |   Execute the query instructions.  This is called by a g_list_foreach
    853  |   function, so each of the sources in the "database source" list can be passed
    854  |   into this function.
    855  |
    856  |   This function has bloated itself.  Can we split it up Marek?  (ottrey 13/12/99)
    857  |
    858  |   void *database_voidptr Pointer to the database.
    859  |
    860  |   void *qis_voidptr Pointer to the query_instructions.
    861  |
    862  |   More:
    863  |   +html+ <PRE>
    864  |   Authors:
    865  |         ottrey
    866  |   +html+ </PRE><DL COMPACT>
    867  |   +html+ <DT>Online References:
    868  |   +html+ <DD><UL>
    869  |              <LI><A
    870  |              HREF="http://www.gtk.org/rdp/glib/glib-singly-linked-lists.html#G-SLIST-FOREACH">g_list_foreach</A>
    871  |   +html+ </UL></DL>
    872  |
    873  |   ++++++++++++++++++++++++++++++++++++++*/
    874  | void QI_execute(void *database_voidptr,
    875  | 		Query_instructions *qis,
    876  | 		Query_environ *qe,
    877  | 		acc_st *acc_credit
    878  | 		) {
    879  |   char *database = (char *)database_voidptr;
    880  |   Query_instruction **ins=NULL;
    881  |   char id_table[STR_S];
    882  |   char sql_command[STR_XL];
    883  |   GList *datlist=NULL;
    884  |   int i;
    885  |   SQ_row_t *row;
    886  |   char log_str[STR_L];
    887  |   SQ_result_set_t *result;
    888  |   SQ_connection_t *sql_connection=NULL;
    889  |
    890  |   sql_connection = SQ_get_connection(CO_get_host(), CO_get_database_port(), database, CO_get_user(), CO_get_password() );
    891  |
    892  |   if (sql_connection == NULL) {
    893  |     SK_cd_puts(&(qe->condat), "% WARNING: Failed to make connection to ");
    894  |     SK_cd_puts(&(qe->condat), database);
    895  |     SK_cd_puts(&(qe->condat), " database mirror.\n\n");
    896  |
    897  |     /* XXX void prevents us from sending any error code back. It is OK ? */
    898  |     return;
    899  |   }
    900  |
    901  |   /* XXX This is a really bad thing to do.
    902  |      It should'nt _have_ to be called here.
    903  |      But unfortunately it does.   -- Sigh. */
    904  |   sprintf(id_table, "ID_%d", mysql_thread_id(sql_connection) );
    905  |
    906  |   /* create a table for id's of all objects found */
    907  |   sprintf(sql_command, "CREATE TABLE %s ( id int ) TYPE=HEAP", id_table);
    908  |   SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
    909  |
    910  |   /* create a table for individual subqueries (one keytype) */
    911  |   sprintf(sql_command, "CREATE TABLE %s_S ( id int ) TYPE=HEAP", id_table);
    912  |   SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
    913  |
    914  |   /* Iterate through query instructions */
    915  |   ins = qis->instruction;
    916  |   for (i=0; ins[i] != NULL; i++) {
    917  |     Query_instruction *qi = ins[i];
    918  |
    919  |     switch ( qi->search_type ) {
    920  |     case R_SQL:
    921  |       if ( qi->query_str != NULL ) {
    922  |
    923  | 	/* handle special cases first */
    924  | 	if( Query[qi->queryindex].class == C_DN ) {
    925  |
    926  | 	  char *countstr;
    927  | 	  int   count;
    928  |
    929  | 	  /* XXX if any more cases than just domain appear, we will be
    930  | 	     cleaning the _S table from the previous query here */
    931  |
    932  | 	  /* now query into the _S table */
    933  | 	  sprintf(sql_command, "INSERT INTO %s_S %s", id_table, qi->query_str);
    934  | 	  SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
    935  |
    936  | 	  /* if any results - copy to the id's table.
    937  | 	     Otherwise, run referral */
    938  |
    939  | 	  sprintf(sql_command, "SELECT COUNT(*) FROM %s_S", id_table);
    940  | 	  result = SQ_execute_query(SQ_STORE,sql_connection, sql_command);
    941  | 	  row = SQ_row_next(result);
    942  | 	  countstr = SQ_get_column_string(result, row, 0);
    943  | 	  sscanf(countstr, "%d", &count);
    944  | 	  SQ_free_result(result);
    945  |
    946  | 	  sprintf(log_str, "DN lookup for %s found %d entries\n",
    947  | 		  qis->qc->keys, count);
    948  | 	  log_inst_print(log_str);
    949  |
    950  |
    951  | 	  if( count ) {
    952  | 	    sprintf(sql_command, "INSERT INTO %s SELECT id FROM %s_S",
    953  | 		    id_table, id_table);
    954  | 	    SQ_execute_query(SQ_NOSTORE,sql_connection, sql_command);
    955  | 	  }
    956  |
    957  | 	  if( count == 0
    958  | 	      || Query[qi->queryindex].querytype == Q_INVERSE ) {
    959  | 	    /* now: if the domain was not found, we run referral.
    960  | 	       unless prohibited by a flag
    961  |
    962  | 	       But for inverse queries we return the things that were
    963  | 	       or were not found AND also do the referral unless prohibited.
    964  | 	    */
    965  | 	    if (qis->qc->R == 0) {
    966  | 	      run_referral(sql_connection, qis, qe, i);
    967  | 	    }
    968  | 	  }
    969  |
    970  | 	}
    971  | 	else {
    972  | 	  sprintf(sql_command, "INSERT INTO %s %s", id_table, qi->query_str);
    973  | 	  SQ_execute_query(SQ_NOSTORE,sql_connection, sql_command);
    974  | 	}
    975  |       }
    976  |       break;
    977  |
    978  | #define RIPE_REG 17
    979  |     case R_RADIX:
    980  |       if ( RX_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0, qi->rx_keys, RIPE_REG, qi->space, qi->family, &datlist, RX_ANS_ALL) == RX_OK ) {
    981  | 	sprintf(log_str, "After RX query (mode %d par %d spc %d fam %d reg %d key %s) datlist has %d objects\n",
    982  | 		qi->rx_srch_mode, qi->rx_par_a, qi->space, qi->family,
    983  | 		RIPE_REG, 	qi->rx_keys,
    984  | 		g_list_length(datlist) );
    985  | 	log_inst_print(log_str);
    986  |
    987  |       }
    988  |       else {
    989  | 	/* Skip query */
    990  | 	sprintf(log_str, " /* Skip in query */\n");
    991  | 	log_inst_print(log_str);
    992  |       }
    993  |       break;
    994  |
    995  |     default: die;
    996  |     } /* switch */
    997  |   }
    998  |
    999  |   /* post-processing */
    1000 |
    1001 |   if( qis->filtered == 0 ) {
    1002 |     /* add radix results to the end */
    1003 |     insert_radix_serials(sql_connection, id_table, datlist);
    1004 |
    1005 |     /* display objects */
    1006 |     write_objects(sql_connection, id_table, qis->recursive, 0 /*nofilter*/,
    1007 | 		  qis->fast, &(qe->condat), acc_credit);
    1008 |   }
    1009 |   else {
    1010 |     /* XXX TODO display filtered objects, expanding sets */
    1011 |     /* right now only the ugly filter thing instead */
    1012 |
    1013 |     /* write_set_objects */
    1014 |
    1015 |     /* write the remaining (non-set,non-radix) objects through the filter.
    1016 |        imply no recursion */
    1017 |     write_objects(sql_connection, id_table, 0, qis->filtered, qis->fast, &(qe->condat), acc_credit);
    1018 |
    1019 |     /* display the immediate data from the radix tree */
    1020 |     /* XXX pass+decrease credit here */
    1021 |     write_radix_immediate(datlist, &(qe->condat));
    1022 |   }
    1023 |   /* Now drop the _S table */
    1024 |   sprintf(sql_command, "DROP TABLE %s_S", id_table);
    1025 |   SQ_execute_query(SQ_NOSTORE,sql_connection, sql_command);
    1026 |
    1027 |   /* Now drop the IDS table */
    1028 |   sprintf(sql_command, "DROP TABLE %s", id_table);
    1029 |   SQ_execute_query(SQ_NOSTORE,sql_connection, sql_command);
    1030 |   SQ_close_connection(sql_connection);
    1031 |
    1032 |
    1033 | } /* QI_execute() */
    1034 | /* instruction_free() */
    1035 | /*++++++++++++++++++++++++++++++++++++++
    1036 |   Free the instruction.
    1037 |
    1038 |   Query_instruction *qi query_instruction to be freed.
    1039 |
    1040 |   More:
    1041 |   +html+ <PRE>
    1042 |   Authors:
    1043 |         ottrey
    1044 |   +html+ </PRE><DL COMPACT>
    1045 |   +html+ <DT>Online References:
    1046 |   +html+ <DD><UL>
    1047 |   +html+ </UL></DL>
    1048 |
    1049 |   ++++++++++++++++++++++++++++++++++++++*/
    1050 | static void instruction_free(Query_instruction *qi) {
    1051 |   if (qi != NULL) {
    1052 |     if (qi->query_str != NULL) {
    1053 |       free(qi->query_str);
    1054 |     }
    1055 |     free(qi);
    1056 |   }
    1057 | } /* instruction_free() */
    1058 |
    1059 | /* QI_free() */
    1060 | /*++++++++++++++++++++++++++++++++++++++
    1061 |   Free the query_instructions.
    1062 |
    1063 |   Query_instructions *qis Query_instructions to be freed.
    1064 |
    1065 |   XXX This isn't working too well at the moment.
    1066 |
    1067 |   More:
    1068 |   +html+ <PRE>
    1069 |   Authors:
    1070 |         ottrey
    1071 |   +html+ </PRE><DL COMPACT>
    1072 |   +html+ <DT>Online References:
    1073 |   +html+ <DD><UL>
    1074 |   +html+ </UL></DL>
    1075 |
    1076 |   ++++++++++++++++++++++++++++++++++++++*/
    1077 | void QI_free(Query_instructions *qis) {
    1078 |   /* XXX huh!?H?
    1079 |   int i;
    1080 |
    1081 |   for (i=0; qis[i] != NULL; i++) {
    1082 |     instruction_free(*qis[i]);
    1083 |   }
    1084 |   */
    1085 |   if (qis != NULL) {
    1086 |     free(qis);
    1087 |   }
    1088 |
    1089 | } /* QI_free() */
    1090 |
    1091 | /*++++++++++++++++++++++++++++++++++++++
    1092 |   Determine if this query should be conducted or not.
    1093 |
    1094 |   If it was an inverse query - it the attribute appears in the query command's bitmap.
    1095 |   If it was a lookup query - if the attribute appears in the object type bitmap or
    1096 |                              disregard if there is no object_type bitmap (Ie object filter).
    1097 |
    1098 |   mask_t bitmap The bitmap of attribute to be converted.
    1099 |
    1100 |   const Query_command *qc The query_command that the instructions are created
    1101 |                           from.
    1102 |
    1103 |   const Query_t q The query being investigated.
    1104 |
    1105 |   ++++++++++++++++++++++++++++++++++++++*/
    1106 | static int valid_query(const Query_command *qc, const Query_t q) {
    1107 |   int result=0;
    1108 |
    1109 |   if (MA_isset(qc->keytypes_bitmap, q.keytype) == 1) {
    1110 |     if (q.query != NULL) {
    1111 |       switch (q.querytype) {
    1112 |         case Q_INVERSE:
    1113 |           if (MA_isset(qc->inv_attrs_bitmap, q.attribute) ) {
    1114 |             result = 1;
    1115 |           }
    1116 |         break;
    1117 |
    1118 |         case Q_LOOKUP:
    1119 |           if (MA_bitcount(qc->object_type_bitmap) == 0) {
    1120 |             result=1;
    1121 |           }
    1122 |           else if (MA_isset(qc->object_type_bitmap, q.class)) {
    1123 |             result=1;
    1124 |           }
    1125 |         break;
    1126 |
    1127 |         default:
    1128 |           fprintf(stderr, "qi:valid_query() -> Bad querytype\n");
    1129 |       }
    1130 |     }
    1131 |   }
    1132 |
    1133 |   return result;
    1134 | } /* valid_query() */
    1135 |
    1136 | /* QI_new() */
    1137 | /*++++++++++++++++++++++++++++++++++++++
    1138 |   Create a new set of query_instructions.
    1139 |
    1140 |   const Query_command *qc The query_command that the instructions are created
    1141 |                           from.
    1142 |
    1143 |   const Query_environ *qe The environmental variables that they query is being
    1144 |                           performed under.
    1145 |   More:
    1146 |   +html+ <PRE>
    1147 |   Authors:
    1148 |         ottrey
    1149 |   +html+ </PRE><DL COMPACT>
    1150 |   +html+ <DT>Online References:
    1151 |   +html+ <DD><UL>
    1152 |   +html+ </UL></DL>
    1153 |
    1154 |   ++++++++++++++++++++++++++++++++++++++*/
    1155 | Query_instructions *QI_new(const Query_command *qc, const Query_environ *qe) {
    1156 |   Query_instructions *qis=NULL;
    1157 |   Query_instruction *qi=NULL;
    1158 |   int i_no=0,j;
    1159 |   int i;
    1160 |   char *query_str;
    1161 |
    1162 |   char log_str[STR_L];
    1163 |
    1164 |   qis = (Query_instructions *)calloc(1, sizeof(Query_instructions));
    1165 |
    1166 |   qis->filtered = qc->filtered;
    1167 |   qis->fast = qc->fast;
    1168 |   qis->recursive = qc->recursive;
    1169 |   qis->qc = (qc);
    1170 |
    1171 |
    1172 |   for (i=0; Query[i].query != NULL; i++) {
    1173 |
    1174 |     /* If a valid query. */
    1175 |     if ( valid_query(qc, Query[i]) == 1) {
    1176 |       qi = (Query_instruction *)calloc(1, sizeof(Query_instruction));
    1177 |
    1178 |       qi->queryindex = i;
    1179 |
    1180 |       /* SQL Query */
    1181 |       if ( Query[i].refer == R_SQL) {
    1182 |         qi->search_type = R_SQL;
    1183 |         query_str = create_query(Query[i], qc);
    1184 |
    1185 |         if (query_str!= NULL) {
    1186 |           qi->query_str = query_str;
    1187 |           qis->instruction[i_no++] = qi;
    1188 |         }
    1189 |       }
    1190 |       /* Radix Query */
    1191 |       else if (Query[i].refer == R_RADIX) {
    1192 |         qi->search_type = R_RADIX;
    1193 |         qi->space = Query[i].space;
    1194 |         qi->family = Query[i].family;
    1195 |
    1196 |         if (map_qc2rx(qi, qc) == 1) {
    1197 |         int j;
    1198 |         int found=0;
    1199 |
    1200 |           /* check that there is no such query yet */
    1201 |           for (j=0; j<i_no; j++) {
    1202 |             Query_instruction *qij = qis->instruction[j];
    1203 |
    1204 |             if( qij->search_type == R_RADIX
    1205 |                 && qij->space       == qi->space
    1206 |                 && qij->family      == qi->family) {
    1207 |               found=1;
    1208 |               break;
    1209 |             }
    1210 |           }
    1211 |
    1212 |           if ( found ) {
    1213 |             /* Discard the Query Instruction */
    1214 |             free(qi);
    1215 |           }
    1216 |           else {
    1217 |             /* Add the query_instruction to the array */
    1218 |             qis->instruction[i_no++] = qi;
    1219 |           }
    1220 |         }
    1221 |       }
    1222 |       else {
    1223 |         sprintf(log_str, "ERROR: bad search_type\n");
    1224 |         log_inst_print(log_str);
    1225 |       }
    1226 |     }
    1227 |   }
    1228 |   qis->instruction[i_no++] = NULL;
    1229 |
    1230 |   return qis;
    1231 |
    1232 | } /* QI_new() */