Source File modules/qi/query_instructions.c

    1    | /***************************************
    2    |   $Revision: 1.41 $
    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 "rp.h"
    48   | #include "stubs.h"
    49   | #include "constants.h"
    50   | #include "memwrap.h"
    51   | #include "wh_queries.h"
    52   |
    53   | /*+ String sizes +*/
    54   | #define STR_S   63
    55   | #define STR_M   255
    56   | #define STR_L   1023
    57   | #define STR_XL  4095
    58   | #define STR_XXL 16383
    59   |
    60   | /* XXX this must be removed from here!!! a .h file must be
    61   |    generated from xml */
    62   |
    63   | #include "defs.h"
    64   |
    65   | /* create_name_query() */
    66   | /*++++++++++++++++++++++++++++++++++++++
    67   |   Create an sql query for the names table.
    68   |
    69   |   char *query_str
    70   |
    71   |   const char *sql_query
    72   |
    73   |   const char *keys
    74   |
    75   |   More:
    76   |   +html+ <PRE>
    77   |   Authors:
    78   |   ottrey
    79   |   +html+ </PRE><DL COMPACT>
    80   |   +html+ <DT>Online References:
    81   |   +html+ <DD><UL>
    82   |   +html+ </UL></DL>
    83   |
    84   |   ++++++++++++++++++++++++++++++++++++++*/
    85   | static void create_name_query(char *query_str, const char *sql_query, const char *keys) {
    86   |   int i;
    87   |   /* Allocate stuff */
    88   |   GString *from_clause = g_string_sized_new(STR_XL);
    89   |   GString *where_clause = g_string_sized_new(STR_XL);
    90   |   gchar **words = g_strsplit(keys, " ", 0);
    91   |
    92   |   /* double quotes " are used in queries to allow querying for
    93   |      names like O'Hara */
    94   |
    95   |   g_string_sprintfa(from_clause, "names N%.2d", 0);
    96   |   g_string_sprintfa(where_clause, "N%.2d.name=\"%s\"", 0, words[0]);
    97   |
    98   |   for (i=1; words[i] != NULL; i++) {
    99   |     g_string_sprintfa(from_clause, ", names N%.2d", i);
    100  |     g_string_sprintfa(where_clause, " AND N%.2d.name=\"%s\" AND N00.object_id = N%.2d.object_id", i, words[i], i);
    101  |   }
    102  |
    103  |   sprintf(query_str, sql_query, from_clause->str, where_clause->str);
    104  |
    105  |   /* Free up stuff */
    106  |   g_strfreev(words);
    107  |   g_string_free(where_clause,/* CONSTCOND */ TRUE);
    108  |   g_string_free(from_clause, /* CONSTCOND */ TRUE);
    109  |
    110  | } /* create_name_query() */
    111  |
    112  | /*+ create_asblock_query:
    113  |
    114  |   given a string like: AS1
    115  |                        AS1 - AS10
    116  | 		       AS1-AS10
    117  |   construct a range query for the as_block table
    118  | */
    119  | static int create_asblock_query(char *query_str,
    120  | 				const char *sql_query,
    121  | 				const char *keys) {
    122  |   char *keycopy = wr_string(keys);
    123  |   char *token, *cursor = keycopy;
    124  |   int  asnums[2] = {0,0};
    125  |   int index = 0; /* index into the asnums array */
    126  |
    127  |
    128  |   while( (token = strsep( &cursor, "-" )) != NULL && index < 2) {
    129  |     if( sscanf(token, "AS%d", &asnums[index++]) < 1 ) {
    130  |       return -1; /* error */
    131  |     }
    132  |   }
    133  |   /* if only beginning was supplied, copy it as end */
    134  |   if( index == 1 ) {
    135  |     asnums[1] = asnums[0];
    136  |   }
    137  |
    138  |   /* now construct the query */
    139  |   sprintf(query_str, sql_query, asnums[0], asnums[1]);
    140  |
    141  |   wr_free(keycopy);
    142  |   return 0;
    143  | }
    144  |
    145  | static void add_filter(char *query_str, const Query_command *qc) {
    146  |   int i;
    147  |   int qlen;
    148  |   char filter_atom[STR_M];
    149  |
    150  | /*
    151  |   if (MA_bitcount(qc->object_type_bitmap) > 0) {
    152  |     g_string_sprintfa(query_str, " AND (");
    153  |     for (i=0; i < C_END; i++) {
    154  |       if (MA_isset(qc->object_type_bitmap, i)) {
    155  |         g_string_sprintfa(query_str, "i.object_type = %d OR ", DF_get_class_dbase_code(i));
    156  |       }
    157  |     }
    158  |     g_string_truncate(query_str, query_str->len-3);
    159  |     g_string_append_c(query_str, ')');
    160  |   }
    161  | */
    162  |   if (MA_bitcount(qc->object_type_bitmap) > 0) {
    163  |     strcat(query_str, " AND (");
    164  |     for (i=0; i < C_END; i++) {
    165  |       if (MA_isset(qc->object_type_bitmap, i)) {
    166  |         strcpy(filter_atom, "");
    167  |         sprintf(filter_atom, "i.object_type = %d OR ", i);
    168  | 	                /* XXX class codes should be used instead:
    169  | 			   DF_get_class_dbase_code(i))
    170  | 			   but currently the tables contain values of enums
    171  | 			   (C_IN, etc) and not codes
    172  | 			*/
    173  |         strcat(query_str, filter_atom);
    174  |       }
    175  |     }
    176  |     qlen = strlen(query_str);
    177  |     query_str[qlen-3] = ')';
    178  |     query_str[qlen-2] = '\0';
    179  |     query_str[qlen-1] = '\0';
    180  |   }
    181  |
    182  | } /* add_filter() */
    183  |
    184  | /* create_query() */
    185  | /*++++++++++++++++++++++++++++++++++++++
    186  |   Create an sql query from the query_command and the matching keytype and the
    187  |   selected inverse attributes.
    188  |   Note this clears the first inv_attribute it sees, so is called sequentially
    189  |   until there are no inv_attributes left.
    190  |
    191  |   WK_Type keytype The matching keytype.
    192  |
    193  |   const Query_command *qc The query command.
    194  |
    195  |   mask_t *inv_attrs_bitmap The selected inverse attributes.
    196  |
    197  |   More:
    198  |   +html+ <PRE>
    199  |   Authors:
    200  |         ottrey
    201  |   +html+ </PRE><DL COMPACT>
    202  |   +html+ <DT>Online References:
    203  |   +html+ <DD><UL>
    204  |   +html+ </UL></DL>
    205  |
    206  |   ++++++++++++++++++++++++++++++++++++++*/
    207  | static char *create_query(const Query_t q, const Query_command *qc) {
    208  |   char *result=NULL;
    209  |   char result_buff[STR_XL];
    210  |   Q_Type_t querytype;
    211  |   int addquery = 0; /* controls if the query should be added to the list */
    212  |
    213  |   if (MA_bitcount(qc->inv_attrs_bitmap) > 0) {
    214  |     querytype = Q_INVERSE;
    215  |   }
    216  |   else {
    217  |     querytype = Q_LOOKUP;
    218  |   }
    219  |
    220  |   if ( (q.query != NULL)
    221  |     && (q.querytype == querytype) ) {
    222  |
    223  |     addquery = 1; /* if it got here, it should be added, unless.(see asblock)*/
    224  |
    225  |     if (q.keytype == WK_NAME) {
    226  |       /* Name queries require special treatment. */
    227  |        create_name_query(result_buff, q.query, qc->keys);
    228  |     }
    229  |     else if( q.keytype == WK_IPADDRESS ) {  /* ifaddr sql lookups */
    230  | 	ip_range_t myrang;
    231  | 	unsigned   begin, end;
    232  | 	ip_keytype_t key_type;
    233  |
    234  | 	if (NOERR(IP_smart_range(qc->keys, &myrang, IP_EXPN, &key_type))) {
    235  | 	    if(IP_rang_b2_space(&myrang) == IP_V4 ) {
    236  | 		IP_rang_b2v4(&myrang, &begin, &end);
    237  | 		sprintf(result_buff, q.query, begin, end);
    238  | 	    }
    239  | 	    else {
    240  | 		die;
    241  | 	    }
    242  | 	}
    243  |     }
    244  |     else if( q.keytype == WK_ASRANGE ) {   /* as_block range composition */
    245  |       if( create_asblock_query(result_buff, q.query, qc->keys) != 0 ) {
    246  | 	addquery = 0; /* ... unless it's not correct */
    247  |       }
    248  |     }
    249  |     else {
    250  |       sprintf(result_buff, q.query, qc->keys);
    251  |     }
    252  |
    253  |     if (q.class == -1 && addquery == 1 ) {
    254  |       /* It is class type ANY so add the object filtering */
    255  |       add_filter(result_buff, qc);
    256  |     }
    257  |   }
    258  |
    259  |   if( addquery == 1 ) {
    260  |     dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK);
    261  |     strcpy(result, result_buff);
    262  |     return result;
    263  |   }
    264  |   else {
    265  |     return NULL;
    266  |   }
    267  | } /* create_query() */
    268  |
    269  | /* fast_output() */
    270  | /*++++++++++++++++++++++++++++++++++++++
    271  |   This is for the '-F' flag.
    272  |   It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
    273  |
    274  |   Fast isn't fast anymore - it's just there for compatibility reasons.
    275  |   This could be speed up if there were breaks out of the loops, once it matched something.
    276  |   (Wanna add a goto Marek?  :-) ).
    277  |
    278  |   const char *string The string to be "fast outputed".
    279  |
    280  |   More:
    281  |   +html+ <PRE>
    282  |   Authors:
    283  |         ottrey
    284  |   +html+ </PRE><DL COMPACT>
    285  |   +html+ <DT>Online References:
    286  |   +html+ <DD><UL>
    287  |   +html+ </UL></DL>
    288  |
    289  |   ++++++++++++++++++++++++++++++++++++++*/
    290  |
    291  | char *fast_output(const char *str)
    292  | {
    293  | int i,j;
    294  | char *result;
    295  | char result_bit[STR_L];
    296  | char result_buff[STR_XL];
    297  | gchar **lines = g_strsplit(str, "\n", 0);
    298  | char * const *attribute_names;
    299  | gboolean filtering_an_attribute = FALSE;
    300  | char *value;
    301  |
    302  | attribute_names = DF_get_attribute_names();
    303  |
    304  | strcpy(result_buff, "");
    305  |  for (j=0; lines[j] != NULL; j++) {
    306  |    for(i=0; attribute_names[i] != NULL; i++) {
    307  |      if (strncmp(attribute_names[i], lines[j], strlen(attribute_names[i])) == 0) {
    308  |        strcpy(result_bit, "");
    309  |        /* This is the juicy bit that converts the likes of; "source: RIPE" to "*so: RIPE" */
    310  |        value = strchr(lines[j], ':');
    311  |        value++;
    312  |        /* Now get rid of whitespace. */
    313  |        while (*value == ' ' || *value == '\t') {
    314  | 	 value++;
    315  |        }
    316  |        sprintf(result_bit, "*%s: %s\n", DF_get_attribute_code(i), value);
    317  |        strcat(result_buff, result_bit);
    318  |      }
    319  |      /* CONSTCOND */
    320  |      else if (filtering_an_attribute == TRUE) {
    321  |        switch (lines[j][0]) {
    322  |        case ' ':
    323  |        case '\t':
    324  |        case '+':
    325  | 	 strcpy(result_bit, "");
    326  | 	 sprintf(result_bit, "%s\n", lines[j]);
    327  | 	 strcat(result_buff, result_bit);
    328  | 	 break;
    329  |
    330  |        default:
    331  | 	 filtering_an_attribute = FALSE;
    332  |        }
    333  |      }
    334  |    }
    335  |  }
    336  |
    337  |
    338  |  dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK);
    339  |
    340  |  strcpy(result, result_buff);
    341  |
    342  |  return result;
    343  | } /* fast_output() */
    344  |
    345  | /* filter() */
    346  | /*++++++++++++++++++++++++++++++++++++++
    347  |   Basically it's for the '-K' flag for non-set (and non-radix) objects.
    348  |   It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
    349  |
    350  |   This could be speed up if there were breaks out of the loops, once it matched something.
    351  |   (Wanna add a goto Marek?  :-) ).
    352  |
    353  |   const char *string The string to be filtered.
    354  |
    355  |   More:
    356  |   +html+ <PRE>
    357  |   Authors:
    358  |         ottrey
    359  |   +html+ </PRE><DL COMPACT>
    360  |   +html+ <DT>Online References:
    361  |   +html+ <DD><UL>
    362  |   +html+ </UL></DL>
    363  |
    364  |   ++++++++++++++++++++++++++++++++++++++*/
    365  | char *filter(const char *str) {
    366  |   int i,j, passed=0;
    367  |   char *result;
    368  |   char result_bit[STR_L];
    369  |   char result_buff[STR_XL];
    370  |   gchar **lines = g_strsplit(str, "\n", 0);
    371  |   char * const *filter_names;
    372  |   gboolean filtering_an_attribute = FALSE;
    373  |
    374  |   filter_names = DF_get_filter_names();
    375  |
    376  |   strcpy(result_buff, "");
    377  |   for (i=0; filter_names[i] != NULL; i++) {
    378  |     for (j=0; lines[j] != NULL; j++) {
    379  |       if (strncmp(filter_names[i], lines[j], strlen(filter_names[i])) == 0) {
    380  |         strcpy(result_bit, "");
    381  |         sprintf(result_bit, "%s\n", lines[j]);
    382  |         strcat(result_buff, result_bit);
    383  | 	passed++;
    384  |
    385  | 	/* can someone explain where %^&()! lint sees the condition here ? */
    386  | 	/* CONSTCOND */
    387  |         filtering_an_attribute = TRUE;
    388  |       }
    389  |       /* CONSTCOND */
    390  |       else if (filtering_an_attribute == TRUE) {
    391  |         switch (lines[j][0]) {
    392  |           case ' ':
    393  |           case '\t':
    394  |           case '+':
    395  |             strcpy(result_bit, "");
    396  |             sprintf(result_bit, "%s\n", lines[j]);
    397  |             strcat(result_buff, result_bit);
    398  |           break;
    399  |
    400  |           default:
    401  |             filtering_an_attribute = FALSE;
    402  |         }
    403  |       }
    404  |     }
    405  |   }
    406  |
    407  |   if(passed) {
    408  |     strcat(result_buff, "\n");
    409  |   }
    410  |
    411  |   dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK);
    412  |   strcpy(result, result_buff);
    413  |
    414  |   return result;
    415  | } /* filter() */
    416  |
    417  | /* write_results() */
    418  | /*++++++++++++++++++++++++++++++++++++++
    419  |   Write the results to the client socket.
    420  |
    421  |   SQ_result_set_t *result The result set returned from the sql query.
    422  |   unsigned filtered       if the objects should go through a filter (-K)
    423  |   sk_conn_st *condat      Connection data for the client
    424  |   int maxobjects          max # of objects to write
    425  |
    426  |   XXX NB. this is very dependendant on what rows are returned in the result!!!
    427  |
    428  |   More:
    429  |   +html+ <PRE>
    430  |   Authors:
    431  |         ottrey
    432  |   +html+ </PRE><DL COMPACT>
    433  |   +html+ <DT>Online References:
    434  |   +html+ <DD><UL>
    435  |   +html+ </UL></DL>
    436  |
    437  |   ++++++++++++++++++++++++++++++++++++++*/
    438  | static int write_results(SQ_result_set_t *result,
    439  | 			 unsigned filtered,
    440  | 			 unsigned fast,
    441  | 			 sk_conn_st *condat,
    442  | 			 acc_st    *acc_credit,
    443  | 			 acl_st    *acl
    444  | 			 ) {
    445  |   SQ_row_t *row;
    446  |   char *str;
    447  |   char *filtrate;
    448  |   char *fasted;
    449  |   int retrieved_objects=0;
    450  |   char *objt;
    451  |   int type;
    452  |
    453  |   /* Get all the results - one at a time */
    454  |   if (result != NULL) {
    455  |     /* here we are making use of the mysql_store_result capability
    456  |        of interrupting the cycle of reading rows. mysql_use_result
    457  |        does not allow that, must be read until end */
    458  |
    459  |       while ( (row = SQ_row_next(result)) != NULL  &&  acc_credit->denials == 0 ) {
    460  | 	  if (  (str = SQ_get_column_string(result, row, 0)) == NULL
    461  | 	     || (objt = SQ_get_column_string(result, row, 3)) == NULL )  {
    462  | 	  /* handle it somehow ? */
    463  | 	  die;
    464  |       }
    465  |       else  {
    466  | 	  /* get + add object type */
    467  | 	  type = atoi(objt);
    468  |
    469  | 	  /* ASP_QI_LAST_DET */
    470  | 	  ER_dbg_va(FAC_QI, ASP_QI_LAST_DET,
    471  | 		    "Retrieved serial id = %d , type = %s", atoi(str), objt);
    472  |
    473  | 	  wr_free(str);
    474  | 	  wr_free(objt);
    475  |       }
    476  |
    477  |       /* decrement credit for accounting purposes */
    478  |
    479  |       /* XXX the definition of private/public should go into the defs (xml) */
    480  |       switch( type ) {
    481  |       case C_PN:
    482  |       case C_RO:
    483  | 	if( acc_credit->private_objects <= 0 && acl->maxbonus != -1 ) {
    484  | 	  /* must be negative, will be subtracted */
    485  | 	  acc_credit->denials = -1;
    486  | 	  continue; /* go to the head of the loop */
    487  | 	}
    488  | 	acc_credit->private_objects --;
    489  | 	break;
    490  |       default:
    491  | 	if( acc_credit->public_objects <= 0 && acl->maxpublic != -1 ) {
    492  | 	  acc_credit->denials = -1;
    493  | 	  continue; /* go to the head of the loop */
    494  | 	}
    495  | 	acc_credit->public_objects --;
    496  |       }
    497  |
    498  |       if ((str = SQ_get_column_string(result, row, 2)) == NULL) { die; }
    499  |       else {
    500  |
    501  |         /* The fast output stage */
    502  |         if (fast == 1) {
    503  |           fasted = fast_output(str);
    504  |           wr_free(str);
    505  |           str = fasted;
    506  |         }
    507  |
    508  |         /* The filtering stage */
    509  |         if (filtered == 0) {
    510  |           SK_cd_puts(condat, str);
    511  | 	  SK_cd_puts(condat, "\n");
    512  |         }
    513  |         else {
    514  |
    515  | 	  /* XXX accounting should be done AFTER that, and not for objects
    516  | 	     filtered out */
    517  |
    518  |           filtrate = filter(str);
    519  |           SK_cd_puts(condat, filtrate);
    520  |           wr_free(filtrate);
    521  |         }
    522  |         retrieved_objects++;
    523  |       }
    524  |       wr_free(str);
    525  |     }
    526  |   }
    527  |
    528  |   return retrieved_objects;
    529  | } /* write_results() */
    530  |
    531  | /* write_objects() */
    532  | /*++++++++++++++++++++++++++++++++++++++
    533  |   This is linked into MySQL by the fact that MySQL doesn't have sub selects
    534  |   (yet).  The queries are done in two stages.  Make some temporary tables and
    535  |   insert into them.  Then use them in the next select.
    536  |
    537  |   SQ_connection_t *sql_connection The connection to the database.
    538  |
    539  |   char *id_table The id of the temporary table (This is a result of the hacky
    540  |                   way we've tried to get MySQL to do sub-selects.)
    541  |
    542  |   sk_conn_st *condat  Connection data for the client
    543  |
    544  |   More:
    545  |   +html+ <PRE>
    546  |   Authors:
    547  |         ottrey
    548  |   +html+ </PRE><DL COMPACT>
    549  |   ++++++++++++++++++++++++++++++++++++++*/
    550  | static void write_objects(SQ_connection_t *sql_connection,
    551  | 			  char *id_table,
    552  | 			  unsigned int filtered,
    553  | 			  unsigned int fast,
    554  | 			  sk_conn_st *condat,
    555  | 			  acc_st    *acc_credit,
    556  | 			  acl_st    *acl
    557  | 			  )
    558  | {
    559  |   /* XXX This should really return a linked list of the objects */
    560  |
    561  |   SQ_result_set_t *result, *order_res;
    562  |   SQ_row_t *order_row;
    563  |   int retrieved_objects=0;
    564  |   char sql_command[STR_XL];
    565  |
    566  | #if 0
    567  |   SQ_execute_query( sql_connection, "SELECT object_type FROM object_order ORDER BY order_code", &order_res );
    568  |   while( (order_row = SQ_row_next(order_res)) != NULL ) {
    569  |     char *object_type = SQ_get_column_string(order_res, order_row, 0);
    570  |     sprintf(sql_command, Q_OBJECTS, id_table, object_type);
    571  |
    572  |     exec/write
    573  |   }
    574  |   SQ_free_result(order_res);
    575  | #endif
    576  |
    577  |   sprintf(sql_command, Q_OBJECTS, id_table);
    578  |
    579  |   dieif(SQ_execute_query(sql_connection, sql_command, &result) == -1 );
    580  |
    581  |   retrieved_objects = write_results(result, filtered, fast, condat,
    582  | 				    acc_credit, acl);
    583  |   SQ_free_result(result);
    584  |
    585  | } /* write_objects() */
    586  |
    587  | /* insert_radix_serials() */
    588  | /*++++++++++++++++++++++++++++++++++++++
    589  |   Insert the radix serial numbers into a temporary table in the database.
    590  |
    591  |   mask_t bitmap The bitmap of attribute to be converted.
    592  |
    593  |   SQ_connection_t *sql_connection The connection to the database.
    594  |
    595  |   char *id_table The id of the temporary table (This is a result of the hacky
    596  |                   way we've tried to get MySQL to do sub-selects.)
    597  |
    598  |   GList *datlist The list of data from the radix tree.
    599  |
    600  |   XXX Hmmmmm this isn't really a good place to free things... infact it's quite nasty.  :-(
    601  |
    602  |   More:
    603  |   +html+ <PRE>
    604  |   Authors:
    605  |         ottrey
    606  |   +html+ </PRE><DL COMPACT>
    607  |   +html+ <DT>Online References:
    608  |   +html+ <DD><UL>
    609  |              <LI><A HREF="http://www.gtk.org/rdp/glib/glib-doubly-linked-lists.html">Glist</A>
    610  |   +html+ </UL></DL>
    611  |
    612  |   ++++++++++++++++++++++++++++++++++++++*/
    613  | static void insert_radix_serials(SQ_connection_t *sql_connection, char *id_table, GList *datlist) {
    614  |   GList    *qitem;
    615  |   char sql_command[STR_XL];
    616  |   int serial;
    617  |
    618  |   for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
    619  |     rx_datcpy_t *datcpy = qitem->data;
    620  |
    621  |     serial = datcpy->leafcpy.data_key;
    622  |
    623  |     sprintf(sql_command, "INSERT INTO %s values (%d)", id_table, serial);
    624  |     dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1);
    625  |
    626  |     wr_free(datcpy->leafcpy.data_ptr);
    627  |   }
    628  |
    629  |   wr_clear_list( &datlist );
    630  |
    631  | } /* insert_radix_serials() */
    632  |
    633  |
    634  | /* write_radix_immediate() */
    635  | /*++++++++++++++++++++++++++++++++++++++
    636  |   Display the immediate data carried with the objects returned by the
    637  |   radix tree.
    638  |
    639  |   GList *datlist      The linked list of dataleaf copies
    640  |   sk_conn_st *condat  Connection data for the client
    641  |   acc_st  *acc_credit Accounting struct
    642  |
    643  | More:
    644  |   +html+ <PRE>
    645  |   Authors:
    646  |         marek
    647  |   +html+ </PRE><DL COMPACT>
    648  |   +html+ <DT>Online References:
    649  |   +html+ <DD><UL>
    650  |   +html+ </UL></DL>
    651  |
    652  |
    653  |   Also free the list of answers.
    654  | */
    655  | static void write_radix_immediate(GList *datlist,
    656  | 				  sk_conn_st *condat,
    657  | 				  acc_st    *acc_credit)
    658  | {
    659  |   GList    *qitem;
    660  |
    661  |   for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
    662  |     rx_datcpy_t *datcpy = qitem->data;
    663  |
    664  |     SK_cd_puts(condat, datcpy->leafcpy.data_ptr );
    665  |     SK_cd_puts(condat, "\n");
    666  |
    667  |     wr_free(datcpy->leafcpy.data_ptr);
    668  |
    669  |     acc_credit->public_objects --;
    670  |   }
    671  |
    672  |   wr_clear_list( &datlist );
    673  | } /* write_radix_immediate() */
    674  |
    675  |
    676  | /* map_qc2rx() */
    677  | /*++++++++++++++++++++++++++++++++++++++
    678  |   The mapping between a query_command and a radix query.
    679  |
    680  |   Query_instruction *qi The Query Instruction to be created from the mapping
    681  |                         of the query command.
    682  |
    683  |   const Query_command *qc The query command to be mapped.
    684  |
    685  |   More:
    686  |   +html+ <PRE>
    687  |   Authors:
    688  |         ottrey
    689  |   +html+ </PRE><DL COMPACT>
    690  |   +html+ <DT>Online References:
    691  |   +html+ <DD><UL>
    692  |   +html+ </UL></DL>
    693  |
    694  |   ++++++++++++++++++++++++++++++++++++++*/
    695  | static int map_qc2rx(Query_instruction *qi, const Query_command *qc) {
    696  |   int result=1;
    697  |
    698  |   qi->rx_keys = qc->keys;
    699  |
    700  |   if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
    701  |     qi->rx_srch_mode = RX_SRCH_EXLESS;
    702  |       qi->rx_par_a = 0;
    703  |   }
    704  |   else if ( (qc->L == 1) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
    705  |     qi->rx_srch_mode = RX_SRCH_LESS;
    706  |     qi->rx_par_a = RX_ALL_DEPTHS;
    707  |   }
    708  |   else if ( (qc->L == 0) && (qc->M == 1) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
    709  |     qi->rx_srch_mode = RX_SRCH_MORE;
    710  |       qi->rx_par_a = RX_ALL_DEPTHS;
    711  |   }
    712  |   else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 1) && (qc->m == 0) && (qc->x == 0) ) {
    713  |     qi->rx_srch_mode = RX_SRCH_LESS;
    714  |     qi->rx_par_a = 1;
    715  |   }
    716  |   else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 1) && (qc->x == 0) ) {
    717  |     qi->rx_srch_mode = RX_SRCH_MORE;
    718  |     qi->rx_par_a = 1;
    719  |   }
    720  |   else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 1) ) {
    721  |     qi->rx_srch_mode = RX_SRCH_EXACT;
    722  |     qi->rx_par_a = 0;
    723  |   }
    724  |   else {
    725  |       /* user error  ( XXX : this should have been checked before) */
    726  |
    727  |       ER_dbg_va(FAC_QI, ASP_QI_SKIP,
    728  | 		"ERROR in qc2rx mapping: bad combination of flags");
    729  |       result = 0;
    730  |   }
    731  |
    732  |   return result;
    733  |
    734  | } /* map_qc2rx() */
    735  |
    736  | /* run_referral() */
    737  | /*
    738  |    invoked when no such domain found. Goes through the domain table
    739  |    and searches for shorter domains, then if it finds one with referral
    740  |    it performs it, otherwise it just returns nothing.
    741  |
    742  |    to perform referral, it actually composes the referral query
    743  |    for a given host/port/type and calls the whois query function.
    744  |
    745  |    Well, it returns nothing anyway (void). It just prints to the socket.
    746  |
    747  | */
    748  | void run_referral(SQ_connection_t *sql_connection,
    749  | 		  Query_instructions *qis,
    750  | 		  Query_environ *qe,
    751  | 		  int qi_index) {
    752  |   char *dot = qis->qc->keys;
    753  |   char querystr[STR_L];
    754  |   SQ_row_t *row;
    755  |   SQ_result_set_t *result;
    756  |   char sql_command[STR_XL];
    757  |   int stop_loop=0;
    758  |   char *ref_host;
    759  |   char *ref_type;
    760  |   char *ref_port;
    761  |   int  ref_port_int;
    762  |
    763  |   strcpy(querystr,"");
    764  |
    765  |   while( !stop_loop && (dot=index(dot,'.')) != NULL ) {
    766  |     dot++;
    767  |
    768  |     ER_dbg_va(FAC_QI, ASP_QI_REF_DET, "run_referral: checking %s", dot);
    769  |
    770  |     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);
    771  |     dieif( SQ_execute_query(sql_connection, sql_command, &result) == -1);
    772  |
    773  |     switch( SQ_num_rows(result) ) {
    774  |       case 0: /* no such domain -> no action, will try next chunk */
    775  |       break;
    776  |
    777  |       case 1: /* check for referral host and perform query if present
    778  |                in any case end the loop */
    779  |       stop_loop=1;
    780  |       assert( (row = SQ_row_next(result)) != NULL);
    781  |
    782  |       ref_host = SQ_get_column_string(result, row, 4);
    783  |
    784  |       ER_dbg_va(FAC_QI, ASP_QI_REF_GEN, "referral host is %s", ref_host);
    785  |
    786  |       if( ref_host != NULL && strlen(ref_host) > 0 ) {
    787  |         ref_type = SQ_get_column_string(result, row, 2);
    788  |         ref_port = SQ_get_column_string(result, row, 3);
    789  |
    790  |         /* get the integer value, it should be correct */
    791  |         if( sscanf( ref_port, "%d",&ref_port_int) < 1 ) {
    792  |           die;
    793  |         }
    794  |
    795  |         /* compose the query: */
    796  |
    797  |         /* put -r if the reftype is RIPE and -r or -i were used */
    798  |         if( strcmp(ref_type,"RIPE") == 0
    799  |             && (   Query[qis->instruction[qi_index]->queryindex]
    800  |                    .querytype == Q_INVERSE
    801  |                    || qis->recursive > 0  )   ) {
    802  |           strcat(querystr," -r ");
    803  |         }
    804  |
    805  |         /* prepend with -Vversion,IP for type CLIENTADDRESS */
    806  |         if( strcmp(ref_type,"CLIENTADDRESS") == 0 ) {
    807  |           char optv[STR_M];
    808  |
    809  |           snprintf(optv,STR_M," -V%s,%s ","RIP0.88", qe->condat.ip);
    810  |           strcat(querystr,optv);
    811  |         }
    812  |
    813  |         /* now set the search term - set to the stripped down version
    814  |            for inverse query, full-length otherwise */
    815  |         if( Query[qis->instruction[qi_index]->queryindex].querytype == Q_INVERSE ) {
    816  |           strcat(querystr,dot);
    817  |         }
    818  |         else {
    819  |           strcat(querystr,qis->qc->keys);
    820  |         }
    821  |
    822  | 	SK_cd_puts(&(qe->condat), "% Please note: this information is not stored in the RIPE database\n%\n% connecting to the remote referral site ");
    823  | 	SK_cd_puts(&(qe->condat), ref_host);
    824  | 	SK_cd_puts(&(qe->condat), "\n\n");
    825  |
    826  |         /* WH_sock(sock, host, port, query, maxlines, timeout)) */
    827  |         switch( WH_sock(qe->condat.sock, ref_host, ref_port_int, querystr,  25, 5) ) {
    828  | 	case WH_TIMEOUT:
    829  | 	    SK_cd_puts(&(qe->condat),"referral timeout\n");
    830  | 	    break;
    831  |
    832  | 	case WH_MAXLINES:
    833  | 	    SK_cd_puts(&(qe->condat),"referral maxlines exceeded\n");
    834  | 	    break;
    835  |
    836  | 	case WH_BADHOST:
    837  | 	    SK_cd_puts(&(qe->condat),"referral host not found\n");
    838  | 	    break;
    839  |
    840  | 	case WH_CONNECT:
    841  | 	    SK_cd_puts(&(qe->condat),"referral host not responding\n");
    842  | 	    break;
    843  |
    844  | 	case WH_BIND:
    845  | 	case WH_SOCKET:
    846  | 	    /* XXX internal server problem... what to do - wait ? */
    847  | 	default:
    848  |           ;
    849  |         } /*switch WH_sock */
    850  |       }
    851  |       break;
    852  |
    853  |       default: /* more than one domain in this file: something broken */
    854  |       die;
    855  |     }
    856  |     SQ_free_result(result);
    857  |   }
    858  | } /*run_referral*/
    859  |
    860  | static
    861  | void
    862  | add_ref_name(SQ_connection_t *sql_connection,
    863  | 	     char *rectable,
    864  | 	     char *allnames
    865  | 	     )
    866  | {
    867  |   /* construct the query, allow zero-length list */
    868  |   if( strlen(allnames) > 0 ) {
    869  |     char final_query[STR_XL];
    870  |     char select_query[STR_XL];
    871  |
    872  |     create_name_query(select_query, "SELECT N00.object_id FROM %s WHERE %s "
    873  | 		      "AND N00.object_type != 100 AND N00.thread_id = 0",
    874  | 		      allnames);
    875  |
    876  |     sprintf(final_query, "INSERT INTO %s %s",
    877  | 	    rectable,
    878  | 	    select_query);
    879  |
    880  |     dieif(SQ_execute_query(sql_connection, final_query, NULL) == -1 );
    881  |
    882  |     allnames[0]=0;
    883  |   }
    884  | }
    885  |
    886  | static
    887  | void
    888  | qi_collect_ids(SQ_connection_t *sql_connection,
    889  | 	       Query_instructions *qis,
    890  | 	       Query_environ *qe,
    891  | 	       char *id_table,
    892  | 	       GList **datlist,
    893  | 	       acc_st *acc_credit
    894  | 	       )
    895  | {
    896  |   Query_instruction **ins=NULL;
    897  |   int i;
    898  |   int   count, errors=0;
    899  |   char sql_command[STR_XL];
    900  |   er_ret_t err;
    901  |   char sub_table[32];
    902  |
    903  |   sprintf(sub_table, "%s_S ", id_table);
    904  |
    905  |   /* see if there was a leftover table from a crashed session
    906  |    * (assume the ID cannot be currently in use)
    907  |    */
    908  |   sprintf(sql_command, "DROP TABLE IF EXISTS %s", sub_table);
    909  |   dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
    910  |
    911  |   /* create a table for special subqueries (domain only for now) */
    912  |   sprintf(sql_command, "CREATE TABLE %s ( id int ) TYPE=HEAP", sub_table);
    913  |   dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
    914  |
    915  |   /* Iterate through query instructions */
    916  |   ins = qis->instruction;
    917  |   for (i=0; ins[i] != NULL && errors == 0; i++) {
    918  |     Query_instruction *qi = ins[i];
    919  |
    920  |     switch ( qi->search_type ) {
    921  |     case R_SQL:
    922  |       if ( qi->query_str != NULL ) {
    923  |
    924  | 	/* handle special cases first */
    925  | 	if( Query[qi->queryindex].class == C_DN ) {
    926  |
    927  | 	  /* XXX if any more cases than just domain appear, we will be
    928  | 	     cleaning the _S table from the previous query here
    929  |
    930  | 	     "DELETE FROM %s_S"
    931  | 	  */
    932  |
    933  | 	  /* now query into the _S table */
    934  | 	  sprintf(sql_command, "INSERT INTO %s%s", sub_table, qi->query_str);
    935  | 	  dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1);
    936  |
    937  | 	  /* if any results - copy to the id's table.
    938  | 	     Otherwise, run referral */
    939  | 	  count = SQ_get_affected_rows(sql_connection);
    940  |
    941  | 	  ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
    942  | 		    "DN lookup for %s found %d entries", qis->qc->keys, count);
    943  |
    944  | 	  if( count ) {
    945  | 	    sprintf(sql_command, "INSERT INTO %s SELECT id FROM %s",
    946  | 		    id_table, sub_table);
    947  | 	    dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1);
    948  | 	  }
    949  |
    950  | 	  if( count == 0
    951  | 	      || Query[qi->queryindex].querytype == Q_INVERSE ) {
    952  | 	    /* now: if the domain was not found, we run referral.
    953  | 	       unless prohibited by a flag
    954  |
    955  | 	       But for inverse queries we return the things that were
    956  | 	       or were not found AND also do the referral unless prohibited.
    957  | 	    */
    958  | 	    if (qis->qc->R == 0) {
    959  | 	      run_referral(sql_connection, qis, qe, i);
    960  | 	    }
    961  | 	  }
    962  |
    963  | 	} /* if class DN */
    964  | 	else {
    965  | 	  sprintf(sql_command, "INSERT INTO %s %s", id_table, qi->query_str);
    966  | 	  if(SQ_execute_query(sql_connection, sql_command, NULL) == -1 ) {
    967  | 	    ER_perror(FAC_QI, QI_SQLERR," query='%s' [%d] %s",
    968  | 		      sql_command,
    969  | 		      SQ_errno(sql_connection), SQ_error(sql_connection));
    970  | 	    errors++;
    971  | 	  }
    972  | 	  count = SQ_get_affected_rows(sql_connection);
    973  | 	} /* not DN */
    974  |       } /* if SQL query not NULL */
    975  |
    976  |       ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
    977  | 		"%d entries added in %s query for %s",
    978  | 		count, Query[qi->queryindex].descr, qis->qc->keys
    979  | 		);
    980  |       break;
    981  |
    982  | #define RIPE_REG 17
    983  |     case R_RADIX:
    984  |
    985  |       if( ! qis->qc->S ) /* XXX patch: use new search algorithm by default */ {
    986  | 	err = RP_new_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0,
    987  | 			    qi->rx_keys, RIPE_REG,
    988  | 			    Query[qi->queryindex].attribute,
    989  | 			    datlist, RX_ANS_ALL);
    990  |
    991  |       }
    992  |       else {
    993  | 	err = RP_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0,
    994  | 			    qi->rx_keys, RIPE_REG,
    995  | 			    Query[qi->queryindex].attribute,
    996  | 			    datlist, RX_ANS_ALL);
    997  |       }
    998  |
    999  |       if( NOERR(err)) {
    1000 | 	if( ER_is_traced(FAC_QI, ASP_QI_COLL_DET ) ) {
    1001 | 	  /* prevent unnecessary g_list_length call */
    1002 |
    1003 | 	  ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
    1004 | 		    "%d entries after %s (mode %d par %d reg %d) query for %s",
    1005 | 		    g_list_length(*datlist),
    1006 | 		    Query[qi->queryindex].descr,
    1007 | 		    qi->rx_srch_mode, qi->rx_par_a,
    1008 | 		    RIPE_REG,
    1009 | 		    qi->rx_keys);
    1010 | 	}
    1011 |       }
    1012 |       else {
    1013 | 	ER_inf_va(FAC_QI, ASP_QI_COLL_DET,
    1014 | 		  "RP_asc_search returned %x ", err);
    1015 |       }
    1016 |       break;
    1017 |
    1018 |     default: die;
    1019 |     } /* switch */
    1020 |   } /* for <every instruction> */
    1021 |
    1022 |   /* Now drop the _S table */
    1023 |   sprintf(sql_command, "DROP TABLE %s", sub_table);
    1024 |   dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
    1025 |
    1026 | }
    1027 |
    1028 | static
    1029 | void
    1030 | qi_fetch_references(SQ_connection_t *sql_connection,
    1031 | 		    char *id_table,
    1032 | 		    acc_st *acc_credit
    1033 | 		    )
    1034 | {
    1035 | char rec_table[32];
    1036 |     SQ_result_set_t *result;
    1037 |     SQ_row_t *row;
    1038 |     int thisid = 0;
    1039 |     int oldid = 0;
    1040 |     char allnames[STR_M];
    1041 |     char sql_command[STR_XL];
    1042 |
    1043 |     sprintf(rec_table, "%s_R", id_table);
    1044 |
    1045 |     /* see if there was a leftover table from a crashed session
    1046 |      * (assume the ID cannot be currently in use)
    1047 |      */
    1048 |     sprintf(sql_command, "DROP TABLE IF EXISTS %s ", rec_table);
    1049 |     dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
    1050 |
    1051 |     /* a temporary table for recursive data must be created, because
    1052 |        a query using the same table as a source and target is illegal
    1053 |        ( like: INSERT into ID_123 SELECT * FROM ID_123,admin_c WHERE ... )
    1054 |     */
    1055 |     sprintf(sql_command, "CREATE TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", rec_table);
    1056 |     dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
    1057 |
    1058 |     /* find the contacts */
    1059 |     sprintf(sql_command, Q_REC, rec_table, id_table, "author");
    1060 |     dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
    1061 |
    1062 |     sprintf(sql_command, Q_REC, rec_table, id_table, "admin_c");
    1063 |     dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
    1064 |
    1065 |     sprintf(sql_command, Q_REC, rec_table, id_table, "tech_c" );
    1066 |     dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
    1067 |
    1068 |     sprintf(sql_command, Q_REC, rec_table, id_table, "zone_c" );
    1069 |     dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
    1070 |
    1071 |
    1072 |     /* replace references to dummies by references by name */
    1073 |     sprintf(sql_command,
    1074 | 	    " SELECT id, name    FROM %s IDS STRAIGHT_JOIN names "
    1075 | 	    " WHERE IDS.id = names.object_id "
    1076 | 	    "      AND names.object_type = 100"
    1077 | 	    " ORDER BY id",
    1078 | 	    rec_table);
    1079 |
    1080 |     dieif(SQ_execute_query(sql_connection, sql_command, &result) == -1 );
    1081 |
    1082 |     allnames[0]=0;
    1083 |     /* now go through the results and collect names */
    1084 |     while ( (row = SQ_row_next(result)) != NULL ) {
    1085 |       char *id   = SQ_get_column_string(result, row, 0);
    1086 |       char *name = SQ_get_column_string(result, row, 1);
    1087 |
    1088 |       thisid = atoi(id);
    1089 |
    1090 |       /* when the id changes, the name is complete */
    1091 |       if( thisid != oldid && oldid != 0 ) {
    1092 | 	add_ref_name( sql_connection, rec_table, allnames);
    1093 |       }
    1094 |
    1095 |       strcat(allnames, name);
    1096 |       strcat(allnames, " ");
    1097 |       oldid = thisid;
    1098 |       wr_free(id);
    1099 |       wr_free(name);
    1100 |     }
    1101 |     /* also do the last name */
    1102 |     add_ref_name( sql_connection, rec_table, allnames);
    1103 |
    1104 |     SQ_free_result(result);
    1105 |
    1106 |     /* now copy things back to the main temporary table   */
    1107 |     sprintf(sql_command, "INSERT INTO %s SELECT * FROM %s",
    1108 | 	    id_table, rec_table);
    1109 |     dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
    1110 |
    1111 |     /* Now drop the IDS recursive table */
    1112 |     sprintf(sql_command, "DROP TABLE %s", rec_table);
    1113 |     dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
    1114 | }
    1115 |
    1116 |
    1117 | /* QI_execute() */
    1118 | /*++++++++++++++++++++++++++++++++++++++
    1119 |   Execute the query instructions.  This is called for each source.
    1120 |
    1121 |   void *database_voidptr Pointer to the database.
    1122 |
    1123 |   void *qis_voidptr Pointer to the query_instructions.
    1124 |
    1125 |   More:
    1126 |   +html+ <PRE>
    1127 |   Authors:
    1128 |         ottrey
    1129 |   +html+ </PRE>
    1130 |   ++++++++++++++++++++++++++++++++++++++*/
    1131 | er_ret_t QI_execute(void *database_voidptr,
    1132 | 		    Query_instructions *qis,
    1133 | 		    Query_environ *qe,
    1134 | 		    acc_st *acc_credit,
    1135 | 		    acl_st *acl
    1136 | 		    )
    1137 | {
    1138 |   char *database = (char *)database_voidptr;
    1139 |   Query_instruction **ins=NULL;
    1140 |   char id_table[STR_S];
    1141 |   char sql_command[STR_XL];
    1142 |   GList *datlist=NULL;
    1143 |   int i;
    1144 |   SQ_connection_t *sql_connection=NULL;
    1145 |   int   count, errors=0;
    1146 |   er_ret_t err;
    1147 |
    1148 |   sql_connection = SQ_get_connection(CO_get_host(), CO_get_database_port(),
    1149 | 				     database,
    1150 | 				     CO_get_user(), CO_get_password() );
    1151 |
    1152 |   if (sql_connection == NULL) {
    1153 |     ER_perror(FAC_QI, QI_CANTDB," database='%s' [%d] %s",
    1154 | 	      database, SQ_errno(sql_connection), SQ_error(sql_connection));
    1155 |     return QI_CANTDB;
    1156 |   }
    1157 |
    1158 |   sprintf(id_table, "ID_%ld", mysql_thread_id(sql_connection) );
    1159 |
    1160 |   /* see if there was a leftover table from a crashed session
    1161 |    * (assume the ID cannot be currently in use)
    1162 |    */
    1163 |   sprintf(sql_command, "DROP TABLE IF EXISTS %s ", id_table);
    1164 |   dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
    1165 |
    1166 |   /* create a table for id's of all objects found NOT NULL , UNIQUE(id) */
    1167 |   sprintf(sql_command, "CREATE TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", id_table);
    1168 |   dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
    1169 |
    1170 |   qi_collect_ids(sql_connection, qis, qe, id_table, &datlist, acc_credit);
    1171 |
    1172 |   /* post-processing */
    1173 |   if( qis->filtered == 0 ) {
    1174 |     /* add radix results (only if -K is not active) */
    1175 |     insert_radix_serials(sql_connection, id_table, datlist);
    1176 |   }
    1177 |
    1178 |   /* fetch recursive objects (ac,tc,zc,ah) */
    1179 |   if ( qis->recursive ) {
    1180 |     qi_fetch_references(sql_connection, id_table, acc_credit);
    1181 |   } /* if recursive */
    1182 |
    1183 |   /* display */
    1184 |   /* -K filtering:
    1185 |    * right now only filtering, no expanding sets like write_set_objects()
    1186 |    */
    1187 |
    1188 |   /* display the immediate data from the radix tree */
    1189 |   if( qis->filtered == 1 ) {
    1190 |     write_radix_immediate(datlist, &(qe->condat), acc_credit );
    1191 |   }
    1192 |
    1193 |   /* display objects from the IDs table */
    1194 |   write_objects(sql_connection, id_table, qis->filtered,
    1195 | 		qis->fast, &(qe->condat), acc_credit, acl);
    1196 |
    1197 |   /* Now drop the IDS table */
    1198 |   sprintf(sql_command, "DROP TABLE %s", id_table);
    1199 |   dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
    1200 |   SQ_close_connection(sql_connection);
    1201 | } /* QI_execute() */
    1202 |
    1203 |
    1204 |
    1205 | /* instruction_free() */
    1206 | /*++++++++++++++++++++++++++++++++++++++
    1207 |   Free the instruction.
    1208 |
    1209 |   Query_instruction *qi query_instruction to be freed.
    1210 |
    1211 |   More:
    1212 |   +html+ <PRE>
    1213 |   Authors:
    1214 |         ottrey
    1215 |   +html+ </PRE>
    1216 |   ++++++++++++++++++++++++++++++++++++++*/
    1217 | static void instruction_free(Query_instruction *qi) {
    1218 |   if (qi != NULL) {
    1219 |     if (qi->query_str != NULL) {
    1220 |       wr_free(qi->query_str);
    1221 |     }
    1222 |     wr_free(qi);
    1223 |   }
    1224 | } /* instruction_free() */
    1225 |
    1226 | /* QI_free() */
    1227 | /*++++++++++++++++++++++++++++++++++++++
    1228 |   Free the query_instructions.
    1229 |
    1230 |   Query_instructions *qis Query_instructions to be freed.
    1231 |
    1232 |   XXX This isn't working too well at the moment.
    1233 |
    1234 |   More:
    1235 |   +html+ <PRE>
    1236 |   Authors:
    1237 |         ottrey
    1238 |   +html+ </PRE>
    1239 |   ++++++++++++++++++++++++++++++++++++++*/
    1240 | void QI_free(Query_instructions *qis) {
    1241 |   int i;
    1242 |
    1243 |   for (i=0; qis->instruction[i] != NULL; i++) {
    1244 |     instruction_free(qis->instruction[i]);
    1245 |   }
    1246 |
    1247 |   if (qis != NULL) {
    1248 |     wr_free(qis);
    1249 |   }
    1250 |
    1251 | } /* QI_free() */
    1252 |
    1253 | /*++++++++++++++++++++++++++++++++++++++
    1254 |   Determine if this query should be conducted or not.
    1255 |
    1256 |   If it was an inverse query - it the attribute appears in the query command's bitmap.
    1257 |   If it was a lookup query - if the attribute appears in the object type bitmap or
    1258 |                              disregard if there is no object_type bitmap (Ie object filter).
    1259 |
    1260 |   mask_t bitmap The bitmap of attribute to be converted.
    1261 |
    1262 |   const Query_command *qc The query_command that the instructions are created
    1263 |                           from.
    1264 |
    1265 |   const Query_t q The query being investigated.
    1266 |
    1267 |   ++++++++++++++++++++++++++++++++++++++*/
    1268 | static int valid_query(const Query_command *qc, const Query_t q) {
    1269 |   int result=0;
    1270 |
    1271 |   if (MA_isset(qc->keytypes_bitmap, q.keytype) == 1) {
    1272 |     if (q.query != NULL) {
    1273 |       switch (q.querytype) {
    1274 |         case Q_INVERSE:
    1275 |           if (MA_isset(qc->inv_attrs_bitmap, q.attribute) ) {
    1276 |             result = 1;
    1277 |           }
    1278 |         break;
    1279 |
    1280 |         case Q_LOOKUP:
    1281 |           if (MA_bitcount(qc->object_type_bitmap) == 0) {
    1282 |             result=1;
    1283 |           }
    1284 |           else if (q.class<0 || MA_isset(qc->object_type_bitmap, q.class)) {
    1285 |             result=1;
    1286 |           }
    1287 |         break;
    1288 |
    1289 |         default:
    1290 |           fprintf(stderr, "qi:valid_query() -> Bad querytype\n");
    1291 |       }
    1292 |     }
    1293 |   }
    1294 |
    1295 |   return result;
    1296 | } /* valid_query() */
    1297 |
    1298 | /* QI_new() */
    1299 | /*++++++++++++++++++++++++++++++++++++++
    1300 |   Create a new set of query_instructions.
    1301 |
    1302 |   const Query_command *qc The query_command that the instructions are created
    1303 |                           from.
    1304 |
    1305 |   const Query_environ *qe The environmental variables that they query is being
    1306 |                           performed under.
    1307 |   More:
    1308 |   +html+ <PRE>
    1309 |   Authors:
    1310 |         ottrey
    1311 |   +html+ </PRE>
    1312 |   ++++++++++++++++++++++++++++++++++++++*/
    1313 | Query_instructions *QI_new(const Query_command *qc, const Query_environ *qe) {
    1314 |   Query_instructions *qis=NULL;
    1315 |   Query_instruction *qi=NULL;
    1316 |   int i_no=0;
    1317 |   int i;
    1318 |   char *query_str;
    1319 |
    1320 |   dieif(wr_calloc( (void **) & qis, 1, sizeof(Query_instructions)) != UT_OK);
    1321 |
    1322 |   qis->filtered = qc->filtered;
    1323 |   qis->fast = qc->fast;
    1324 |   qis->recursive = qc->recursive;
    1325 |   qis->qc = (qc);
    1326 |
    1327 |
    1328 |   for (i=0; Query[i].query != NULL; i++) {
    1329 |
    1330 |     /* If a valid query. */
    1331 |     if ( valid_query(qc, Query[i]) == 1) {
    1332 |
    1333 |       dieif( wr_calloc((void **) &qi, 1, sizeof(Query_instruction)) != UT_OK);
    1334 |
    1335 |       qi->queryindex = i;
    1336 |
    1337 |       /* SQL Query */
    1338 |       if ( Query[i].refer == R_SQL) {
    1339 |         qi->search_type = R_SQL;
    1340 |         query_str = create_query(Query[i], qc);
    1341 |
    1342 |         if (query_str!= NULL) {
    1343 |           qi->query_str = query_str;
    1344 |           qis->instruction[i_no++] = qi;
    1345 |         }
    1346 |       }
    1347 |       /* Radix Query */
    1348 |       else if (Query[i].refer == R_RADIX) {
    1349 |         qi->search_type = R_RADIX;
    1350 |
    1351 |         if (map_qc2rx(qi, qc) == 1) {
    1352 | 	  int j;
    1353 | 	  int found=0;
    1354 |
    1355 |           /* check that there is no such query yet, for example if
    1356 | 	     more than one keytype (wk) matched */
    1357 | 	  for (j=0; j<i_no; j++) {
    1358 | 	    Query_instruction *qij = qis->instruction[j];
    1359 |
    1360 | 	    if(    qij->search_type == R_RADIX
    1361 | 		   && Query[qij->queryindex].attribute
    1362 | 		   == Query[qi ->queryindex].attribute) {
    1363 |
    1364 |               found=1;
    1365 |               break;
    1366 |             }
    1367 |           }
    1368 |
    1369 |           if ( found ) {
    1370 |             /* Discard the Query Instruction */
    1371 |             wr_free(qi);
    1372 |           }
    1373 |           else {
    1374 |             /* Add the query_instruction to the array */
    1375 |             qis->instruction[i_no++] = qi;
    1376 |           }
    1377 |         }
    1378 |       }
    1379 |       else {
    1380 | 	  /* ERROR: bad search_type */
    1381 | 	  die;
    1382 |       }
    1383 |     }
    1384 |   }
    1385 |   qis->instruction[i_no++] = NULL;
    1386 |
    1387 |
    1388 |   {  /* tracing */
    1389 |       char *descrstr = QI_queries_to_string(qis);
    1390 |
    1391 |       ER_dbg_va(FAC_QI, ASP_QI_COLL_GEN, "Queries: %s", descrstr );
    1392 |       wr_free( descrstr );
    1393 |   }
    1394 |
    1395 |   return qis;
    1396 |
    1397 | } /* QI_new() */
    1398 |
    1399 | /* QI_queries_to_string()
    1400 |
    1401 |    returns a list of descriptions for queries that will be performed.
    1402 | */
    1403 |
    1404 | char *QI_queries_to_string(Query_instructions *qis)
    1405 | {
    1406 |    Query_instruction *qi;
    1407 |    int i;
    1408 |    char *resstr = NULL;
    1409 |
    1410 |    dieif( wr_realloc((void **)&resstr, resstr, 2 ) != UT_OK);
    1411 |    strcpy(resstr, "{");
    1412 |
    1413 |    for( i = 0; ( qi=qis->instruction[i] ) != NULL;  i++ ) {
    1414 |        char *descr = Query[qi->queryindex].descr;
    1415 |        int oldres = strlen( resstr );
    1416 |
    1417 |        dieif( wr_realloc((void **)&resstr, resstr, oldres+strlen(descr)+2) != UT_OK);
    1418 |        strcat(resstr, descr);
    1419 |        strcat(resstr, ",");
    1420 |    }
    1421 |    if( i>0 ) {
    1422 |        /* cancel the last comma */
    1423 |        resstr[strlen(resstr)-1] = 0;
    1424 |    }
    1425 |
    1426 |    dieif( wr_realloc((void **)&resstr, resstr, strlen( resstr ) + 2 )
    1427 | 	  != UT_OK);
    1428 |    strcat(resstr, "}");
    1429 |
    1430 |    return resstr;
    1431 | }