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 | }