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