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